Многомерные массивы в C: они зубчатые?
Простой вопрос о языке программирования C (ANSI-C):
Являются ли многомерные массивы в C зубчатыми?
Я имею в виду - мы говорим о "массиве массивов" (один массив указателей на другие адреса в памяти), или это просто "длинный одномерный массив" (который сохраняется последовательно в памяти)?
Что меня беспокоит, так это то, что я уверен, что:
matrix[i][j]
эквивалентно * ( * (matrix + i) + j)
Ответы
Ответ 1
Многомерный массив в C смежный. Следующее:
int m[4][5];
состоит из 4 int[5]
, расположенных рядом друг с другом в памяти.
Массив указателей:
int *m[4];
зазубрен. Каждый указатель может указать (первый элемент) отдельный массив разной длины.
m[i][j]
эквивалентен *(*(m+i)+j)
. См. стандарт C11, раздел 6.5.2.1:
Определение индексного оператора [] состоит в том, что E1 [E2] идентичен (* ((E1) + (E2)))
Таким образом, m[i][j]
эквивалентно (*(m+i))[j]
, что эквивалентно *(*(m+i)+j)
.
Эта эквивалентность существует, потому что в большинстве контекстов выражения типа массива распадаются на указатели на их первый элемент (стандарт C11, 6.3.2.1). m[i][j]
интерпретируется как:
-
m
- массив массивов, поэтому он распадается на указатель на m[0]
, первый подмассив.
-
m+i
является указателем на i
-ный подмассив m
.
-
m[i]
эквивалентен *(m+i)
, разыменовывая указатель на i
-ный подмассив m
. Поскольку это выражение типа массива, оно распадается на указатель на m[i][0]
.
-
m[i][j]
эквивалентен *(*(m+i)+j)
, разыменовывая указатель на j
-й элемент i
-го подмассива <<29 > .
Обратите внимание, что указатели на массивы отличаются от указателей на их первый элемент. m+i
- указатель на массив; это не выражение типа массива, и оно не распадается, будь то указатель на указатель или на любой другой тип.
Ответ 2
Последовательная область памяти:
int arr[N][M];
Непересекающаяся область памяти:
int** arr = malloc(N*sizeof(int*));
for (int i=0; i<N; i++)
arr[i] = malloc(M*sizeof(int));
Вы можете использовать arr
как 2-мерный массив (например, arr[1][2] = 3
) в обоих случаях. Но вы можете безопасно применять большие операции копирования, такие как memset(arr,0,N*M*sizeof(int))
, только в первом случае.
Ответ 3
Это будет зависеть.
Многомерные массивы в C последовательно расположены.
Вы можете создавать зубчатые массивы, если хотите использовать указатели.
Ответ 4
Если вы объявляете многомерный массив, вы получаете "длинный одномерный массив" (который сохраняется последовательно в памяти).
Если вы объявляете указатель на указатель (на указатель....), вы получаете массивы массивов.
Это различие является источником большой путаницы для начинающих программистов C.
Ответ 5
Массив или массивы, такие как int matrix[A][B]
, не зазубрены, так как каждый элемент matrix
является array of B int
.
Вы хотите знать, что результат *(*(matrix+i)+j)
есть и сравнить его с результатом matrix[i][j]
.
Так как тип matrix
равен array of A array of B int
, то выражение matrix+i
является указателем, указывающим на i
th array of B int
of matrix
, а его тип int (*)[B]
. Разыменование этого выражения приводит к array of B int
. Выражение *(matrix+i)+j)
приводит к указателю на j
th int
этого массива. Выражение разыгрыша этого выражения приводит к int
. Это эквивалентно выражению matrix[i][j]
.
Массив указателей, таких как int *matrix[A]
, может быть неровным, так как каждый элемент matrix
может указывать на распределение по разному.
Ответ 6
Вы правы, что matrix[i][j]
эквивалентно *(*(matrix + i) + j)
, так как arr[i]
эквивалентен *(arr + i)
. Однако имейте в виду, что если arr
объявлен как
int arr[64];
то любая ссылка на arr
может быть неявно преобразована в &arr[0]
, то есть указатель на первый элемент. То же самое происходит с массивами массивов:
int matrix[8][8];
Здесь matrix
имеет тип int[8][8]
, который автоматически преобразуется в int (*)[8]
, когда вы добавляете к нему целое число, как в matrix + i
. Тогда *(matrix + i)
имеет тип int[8]
, который снова преобразуется в int *
, когда вы добавляете j
, поэтому *(matrix + i) + j
имеет тип int *
, поэтому *(*(matrix + i) + j)
имеет тип int
, как ожидалось.
Итак, дело в том, что массивы не являются указателями, просто они могут быть неявно приведены к указателю на их первый элемент.
Итак, если вы выделяете массивы таких массивов, как указано выше (int matrix[8][8];
), тогда все элементы последовательно хранятся в памяти.