Чтобы полностью понять это, вы должны понять следующие понятия:
Массивы не указатели!
Прежде всего (и это достаточно проповедано), массивы не являются указателями . Вместо этого в большинстве случаев они «распадаются» по адресу к первому элементу, который может быть назначен указателю:
int a[] = {1, 2, 3};
int *p = a; // p now points to a[0]
Я предполагаю, что это работает таким образом, чтобы к содержимому массива можно было обращаться, не копируя их все. Это просто поведение типов массивов, и это не означает, что они - одно и то же.
Многомерные массивы
Многомерные массивы - это просто способ «разделить» память таким образом, чтобы компилятор / машина могли понимать и работать с ней.
Например, int a[4][3][5]
= массив, содержащий 4 * 3 * 5 (60) «кусочков» памяти целого размера.
Преимущество перед использованием int a[4][3][5]
по сравнению с обычным int b[60]
состоит в том, что они теперь «разделены» (проще работать со своими «кусками», если необходимо), и теперь программа может выполнять проверку границ.
На самом деле int a[4][3][5]
хранится точно , как int b[60]
в памяти - разница только в состоит в том, что программа теперь управляет им, как будто они являются отдельными объектами определенных размеры (в частности, четыре группы из трех групп по пять).
Имейте в виду: и int a[4][3][5]
, и int b[60]
одинаковы в памяти, и единственное отличие состоит в том, как они обрабатываются приложением / компилятором
{
{1, 2, 3, 4, 5}
{6, 7, 8, 9, 10}
{11, 12, 13, 14, 15}
}
{
{16, 17, 18, 19, 20}
{21, 22, 23, 24, 25}
{26, 27, 28, 29, 30}
}
{
{31, 32, 33, 34, 35}
{36, 37, 38, 39, 40}
{41, 42, 43, 44, 45}
}
{
{46, 47, 48, 49, 50}
{51, 52, 53, 54, 55}
{56, 57, 58, 59, 60}
}
Из этого вы можете ясно видеть, что каждый «раздел» - это просто массив, который отслеживает программа.
Синтаксис
Теперь массивы синтаксически отличаются от указателей . В частности, это означает, что компилятор / машина будет обрабатывать их по-разному. Это может показаться легким, но взгляните на это:
int a[3][3];
printf("%p %p", a, a[0]);
В приведенном выше примере дважды печатается один и тот же адрес памяти, например:
0x7eb5a3b4 0x7eb5a3b4
Однако указатель может быть назначен только одному так непосредственно :
int *p1 = a[0]; // RIGHT !
int *p2 = a; // WRONG !
Почему a
нельзя назначить указателю, а a[0]
можно?
Это, просто, является следствием многомерных массивов, и я объясню почему:
На уровне a
мы все еще видим, что у нас есть еще одно «измерение», которого мы с нетерпением ждем. На уровне 'a[0]
', однако, мы уже находимся в верхнем измерении, поэтому, что касается программы, мы просто смотрим на обычный массив.
Вы можете спросить:
Почему это важно, если массив многомерен в отношении создания для него указателя?
Лучше думать так:
'Распад' из многомерного массива - это не просто адрес, а адрес с данными раздела (AKA все еще понимает, что его базовые данные сделаны из других массивов), который состоит из установленных границ массивом за пределами первого измерения.
Эта логика «раздела» не может существовать в указателе, если мы не укажем ее:
int a[4][5][95][8];
int (*p)[5][95][8];
p = a; // p = *a[0] // p = a+0
В противном случае значение свойств сортировки массива теряется.
Также обратите внимание на использование круглых скобок вокруг *p
: int (*p)[5][95][8]
- это означает, что мы делаем указатель с этими границами, а не массив указателей с этими границами: int *p[5][95][8]
Заключение
Давайте рассмотрим:
- Массивы распадаются на адреса, если они не имеют никакой другой цели в используемом контексте
- Многомерные массивы - это просто массивы массивов - следовательно, «распавшийся» адрес будет нести бремя «У меня есть субразмеры»
- Данные измерений не могут существовать в указателе , если вы не передадите его .
Вкратце: многомерные массивы распадаются на адреса, которые несут способность понимать их содержимое.