Почему мы не можем использовать двойной указатель для представления двумерных массивов? - PullRequest
18 голосов
/ 17 декабря 2010

Почему мы не можем использовать двойной указатель для представления двумерных массивов?

arr[2][5] = {"hello","hai"};
**ptr = arr;

Вот почему двойной указатель (** ptr) не работает в этом примере?

Ответы [ 5 ]

50 голосов
/ 05 сентября 2011

Я попытаюсь нарисовать, как

int array[10][6];

и

int **array2 = malloc(10 * sizeof *array2);
for (int i = 0; i < 10; ++i)
    array2[i] = malloc(6 * sizeof **array2);

выглядят в памяти и как они различаются (и что их нельзя разыграть

array выглядит так:

 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| | | | | | | | | | | | | ..............| | | (10*6 elements of type int)
 - - - - - - - - - - - - - - - - - - - - - -
< first row >< second row> ...

array2 выглядит так:

 _ _ _ _ _ _ _ _ _ _ 
| | | | | | | | | | | (10 elements of type int *)
 - - - - - - - - - - 
 | |     ....      |     _ _ _ _ _ _
 | |                \-->| | | | | | | (6 elements of type int)
 | |                     - - - - - -
 | |
 | |      _ _ _ _ _ _
 |  \ -->| | | | | | | (6 elements of type int)
 |        - - - - - -
 |
 |
 |      _ _ _ _ _ _
  \ -->| | | | | | | (6 elements of type int)
        - - - - - -

Когда вы говорите array[x][y], это переводится в*((int *)array+x*6+y)

Хотя, когда вы говорите array2[x][y], это переводится в *(*(array2+x)+y) (Обратите внимание, что для array эта формула также работает (прочитайте в конце поста, а затем комментарии)).

То есть статический 2d-массив на самом деле представляет собой 1d-массив со строками, помещенными в одну строку.Индекс рассчитывается по формуле row * number_of_columns_in_one_row + column.

Динамический двумерный массив, однако это всего лишь одномерный массив указателей.Каждый указатель затем динамически распределяется, чтобы указывать на другой 1d массив.По правде говоря, этот указатель может быть чем угодно.Может быть NULL, либо указывать на одну переменную, либо указывать на другой массив.И каждый из этих указателей устанавливается индивидуально, поэтому они могут иметь различную природу.

Если вам нужно куда-то передать указатель array, вы не можете привести его к int ** (представьте, что произойдет. int значения ячеек array интерпретируются как указатели и разыменовываются -> Bam! Ошибка сегментации!).Однако вы можете думать о array как о 1d массиве int [6] s;это массив элементов 1d типа int [6].Чтобы записать это, вы говорите

int (*p)[6] = array;
8 голосов
/ 17 декабря 2010

В C двумерный массив представляет собой массив массивов .

Вам нужен указатель на массив для ссылки на него, а не двойной указатель:

char array[2][6] = {"hello", "hai"};
char (*p)[6] = array;
//char **x = array;  // doesn't compile.

Чтобы указать двойной указатель на «двумерные данные», он должен ссылаться на первый элемент массива указателей . Но двумерный массив в C (массив массивов) - это не то же самое, что массив указателей, и если вы просто определите двумерный массив, то соответствующий массив указателей не существует.

Единственное сходство между ними заключается в синтаксисе [][], используемом для доступа к данным: сами данные структурированы совершенно по-разному.

8 голосов
/ 17 декабря 2010

Наличие указателя на указатель означает, что каждая строка (или столбец, если вы предпочитаете так думать) может иметь длину, отличную от других строк / столбцов.

Вы также можете представить двумерный массив просто указателем на начальный элемент и целым числом, которое указывает количество элементов в строке / столбце:

void matrix_set(double *first, size_t row_size, size_t x, size_t y, double value)
{
  first[y * row_size + x] = value;
}
3 голосов
/ 17 декабря 2010

Создание массива указателей на каждую строку для получения объекта, «похожего» на многомерный массив переменного размера, - это дорогой выбор дизайна ради синтаксического сахара . Не делай этого.

Правильный способ создания многомерного массива переменного размера - это что-то вроде:

if (w > SIZE_MAX/sizeof *m/h) goto error;
m = malloc(w * h * sizeof *m);
if (!m) goto error;
...
m[y*w+x] = foo;

Если вы хотите, чтобы он "выглядел красиво", чтобы вы могли написать m[y][x], вам следует использовать другой язык, возможно, C ++.

1 голос
/ 17 декабря 2010

Давайте начнем с разговора о юридическом кодексе.То, что вы написали (предполагая символ перед каждым объявлением), не будет компилироваться по нескольким причинам: у вас слишком много инициализаторов (шесть символов для arr [0] и его размер равен 5) и, конечно, char** p не имеет типа, совместимого с char arr [2] [5].Исправляя эти проблемы, мы получаем:

char arr[2][6] = { "hello", "hai" };
char (*p)[6] = arr;

Без двойного указателя.Если вы хотите получить доступ к отдельным символам, указанным выше, вам нужно указать элемент, из которого они получены:

char* pc = *arr;

будет работать, если вы хотите получить доступ к символам из первого элемента в обр.

C ++ не имеет двухмерных массивов.Первое определение выше определяет массив [2] или массив [6] символа char.Неявное преобразование массива в указатель приводит к указателю на массив [6] символа.После этого, конечно, нет преобразования массива в указатель, потому что у вас больше нет массива.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...