Как уже указывали другие, многомерный массив - это массив массивов, а не массив указателей.
Я думаю, что основная причина недопонимания заключается в том, что любой массив на самом деле является указателем. Но это не совсем так. Массив - это переменная, в которой хранится непрерывная коллекция фиксированного размера элементов одного типа. Поэтому, когда вы объявляете массив типа int x[2]
, вы объявляете переменную, которая содержит два целых числа. Обратите внимание, что на нем нет указателя.
Теперь корень этого распространенного недоразумения состоит в том, что в C / C ++ имя массива оценивает по адресу его первого элемента и, следовательно, может использоваться как указатель. Другими словами, когда вы пишете x
, вы подразумеваете &x
или &x[0]
. Вероятно, это было сделано для того, чтобы сделать выражения более читабельными.
Многомерный массив - это просто массив массивов. Другими словами, к ним относится та же логика, ничего особенного. Вы читаете объявление C / C ++, начиная с имени и выходя за пределы, применяя модификаторы по мере их появления, сначала [] и (), затем *, затем набираете, так что вы интерпретируете многомерный массив как этот (порядок чтения указан явно ):
int x [ 2] [ 5];
6. "of type int" 1. "x" 2. "is an array" 3. "of 2". 4. "arrays" 5. "of 5 elements"
Таким образом, в C / C ++ нет такой вещи, как многомерные массивы, но есть такая вещь, как одномерный массив одномерных массивов. Согласно правилам для одномерных массивов, x
, &x
и &x[0]
все оценивают по адресу первого элемента. Но поскольку первый элемент является массивом, x[0]
вычисляет адрес этого массива, то есть адрес его первого элемента, который является целым числом. То же самое касается &x[0]
и &x[0][0]
. Вот почему значение x[0]
совпадает с его адресом, поскольку x[0]
является массивом.
Обратите внимание, что хотя эти объекты оцениваются по одному и тому же адресу, они имеют разные типы. x
- это указатель на массив из 5-ти целых чисел, а также &x[0]
, так как оба они оценивают адрес первого элемента x. x[0]
соответствует адресу первого элемента x[0]
, так что это указатель на int, то же самое относится и к &x[0][0]
. Этот пример хорошо компилируется и печатает один и тот же адрес для всех 4 указателей:
int x[2][5];
int (*y1)[5] = x;
int *y2 = x[0];
int (*y3)[5] = &x[0];
int *y4 = &x[0][0];
printf("%p %p %p %p\n", y1, y2, y3, y4);
Теперь существуют разные макеты памяти для массивов, используемых на разных языках. Например, для двумерного массива вы можете сгруппировать элементы по строкам или столбцам. В C / C ++, поскольку нет «истинных» многомерных массивов, макет памяти неявно определяется приведенными выше правилами. Поскольку int x[2][5]
, который можно рассматривать как двумерный массив, имеющий 2 строки и 5 столбцов, на самом деле является массивом из 2 массивов, каждый из которых представляет строку, вы получаете макет «сгруппировать по строкам», который представлены в ответе Шибовича.
Обратите внимание, что также возможно создать массив указателей и использовать его в качестве многомерного массива. Отличия от «обычных» многомерных массивов:
- Массив указателей фактически содержит указатели внутри. То есть адреса первых элементов подмассивов.
- Структура памяти не является смежной. Каждый вложенный массив может быть размещен где угодно, например, с помощью malloc () или new [].
- Подмассивы могут быть разных размеров.
Преимущество этого подхода состоит в том, что вы можете использовать этот массив в качестве указателя на указатель (например, int **y
), что делает все многомерные массивы такого типа совместимыми друг с другом, даже если они имеют разные размеры , Но размеры должны храниться отдельно в этом случае.