Щоб повністю зрозуміти це, ви повинні зрозуміти такі поняття:
Масиви - не покажчики!
Перш за все (і це досить проповідується), масиви не є покажчиками . Натомість у більшості застосувань вони «розпадаються» на адресу свого першого елемента, який може бути призначений покажчиком:
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]
vs plain 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]
Висновок
Розглянемо:
- Масиви розпадаються на адреси, якщо вони не мають іншої мети у використаному контексті
- Багатовимірні масиви - це просто масиви масивів - отже, "розкладена" адреса нестиме тягар "у мене підмірності"
- Дані про розмір не можуть існувати в покажчику, якщо ви не надасте їх .
Якщо коротко: багатовимірні масиви розпадаються на адреси, що несуть можливість розуміти їхній вміст.