Может ли кто-нибудь объяснить мне указатели на указатели - PullRequest
0 голосов
/ 01 декабря 2018

Мы недавно говорили в колледже о указателях на указатели (например: char **ppch), и, насколько я понимаю, их можно использовать как двумерные массивы, поскольку, например, char *pch можно использовать какмассив char / string.

Это тот случай или я что-то упустил?Я не уверен в том, как мы работаем с P2P в программе или мое понимание близко к правильному.

Может кто-нибудь привести пример?Нужно ли нам выделять память с malloc() для каждого элемента в этих P2P?

1 Ответ

0 голосов
/ 01 декабря 2018

Неотъемлемым свойством массивов является то, что их элементы последовательны в памяти.Например,

int foo[] = { 1, 2, 3, 4, 5, /* ... */ };
// ~> 1, 2, 3, 4, 5, ...

2d массив ничем не отличается:

int bar[][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
// ~> 1, 2, 3, 4, 5, 6, 7, 8, 9

Но указатель на указатель некоторого типа

int **qux;

- это только то, что нужно.Указатель на указатель (или количество указателей).Когда вы выделяете память как

int **qux = malloc(N * sizeof(*qux));

, вы получаете указатель на область памяти, достаточно большую, чтобы удерживать N указатели на int, что, в свою очередь, может указывать на другие области памяти:

int **qux = malloc(N * sizeof(*qux));
int value = 0;
for (size_t i = 0; i < N; ++i) {
    qux[i] = malloc(P * sizeof(**qux));
    for (size_t k = 0; k < P; ++k)
        qux[i][k] = ++value;
}

Это выглядит в памяти как

+----------+                              +-----+
| qux[0] --|------------------------------|-> 1 |
| qux[1] --|-----------+                  |   2 |
| qux[2] --|---+       |       +-----+    |   3 |
| ...      |   |       +-------|-> 4 |    +-----+   
+----------+   |               |   5 |       
               |    +-----+    |   6 |
               +----|-> 7 |    +-----+
                    |   8 |
                    |   9 |
                    +-----+

Так что нет, указатель на указатель не является 2d-массивом.Такие вещи скорее называются «зубчатым массивом».

Что касается вашего примера со строками, то здесь нет разницы:

char **qux;  // a pointer to a (number of) pointers to char

qux = malloc(N * sizeof(*foo));

// just an array to fill the memory qux points to with:
char const *source[] = { "foo", "bar", "baz", "qux" /* I'm running out of names */ };
size_t index = 0;

for (size_t i = 0; i < N; ++i) {
    qux[i] = malloc((strlen(source[index]) + 1) * sizeof(**qux));
    strcpy(qux[i], source[index++]);
}

~>

+----------+                                                      +--------+
| foo[0] --|------------------------------------------------------|-> 'f'  |
| foo[1] --|-----------------------------------+                  |   'o'  |
| foo[2] --|----------------------+            |    +--------+    |   'o'  |
| foo[3] --|---+                  |            +----|-> 'b'  |    |   '\0' |   
| ...      |   |                  |                 |   'a'  |    +--------+   
+----------+   |    +--------+    |   +--------+    |   'r'  |
               +----|-> 'q'  |    +---|-> 'b'  |    |   '\0' |
                    |   'u'  |        |   'a'  |    +--------+
                    |   'x'  |        |   'z'  |
                    |   '\0' |        |   '\0' |
                    +--------+        +--------+

В отличие от реального 2-мерного массива char с:

char foo[][4] = { "foo", "bar", "baz", "qux" /*, ... */ };
// ~> 'f', 'o', 'o', '\0', 'b', 'a', 'r', '\0', 'b', 'a', 'z', '\0', 'q', 'u', 'x', '\0', ...
...