Как хранятся в памяти многомерные массивы?
Раньше я думал, что когда мы создаем многомерный массив, например
int arr[2][3] =
{
{1, 2, 3},
{1, 2, 3}
}
в памяти выделяется две ячейки, каждая из которых занимает sizeof(int) * 3 и таким образом можно легко обращаться к любому элементу. Например при обращении к последней тройке arr[1][2], на уровне указателей это будет выглядеть так *(arr + (sizeof(int) * 3 * 1) + 2). Ну и памяти занимает такой массив ровно 2х3.
Но затем я увидел такую формулу на уровне указателей arr[1][2] => *(*(arr + 1) + 2) и пришел к выводу, что многомерный массив хранится в памяти следующим образом (здесь я буду обозначать память вот так 0[5], где 0 - адрес ячейки, [5] - значение, которое хранится внутри ячейки): 0[адрес 2], 1[адрес 5], 2[1], 3[2], 4[3], 5[1], 6[2], 7[3]. И при таком хранении можно легко понять почему в этой формуле *(*(*(arr + i) + j) + k) на каждом уровне идет разыменование. Однако при таком способе хранения тот же массив уже занимает 2 + 2x3.
Так как хранятся многомерные массивы и если описанный мной второй способ верный, то в чем его преимущество над первым?
Ответы (1 шт):
Нет, для массива в памяти хранятся только его элементы, и никаких указателей. int arr[2][3] занимает только sizeof(int) * 2 * 3 байт места.
Но, одновременно с этим, формула arr[1][2] => *(*(arr + 1) + 2) - правильная.
Фокус в том, что нужные указатели вычисляются на лету. Во многих ситуациях массивы автоматически преобразуются в указатель на свой первый элемент, в том числе в этом примере. Получающиеся указатели вычисляются на ходу, и не хранятся постоянно в памяти.
Еще, нужно понимать, что многомерные массивы - это просто обычные (одномерные) массивы, но у которых тип элемента - тоже массив. Для них нет каких-то специальных правил.
Рассмотрим *(*(arr + i) + j).
+сначала преобразует массивarr(типаint[2][3]) в указатель на его первый элемент (как если бы вы сделали&arr[0]), типаint (*)[3]("указатель на массив из 3 int-ов").Затем
+ iсдвигает указатель наi * sizeof(тип-элемента)байт, где тип элемента -int[3], аsizeof(int[3]) == sizeof(int) * 3.Затем
*разыменовывает указатель, и результат имеет типint[3](массив из 3 int-ов).Дальше все то же самое повторяется для второго индекса.
+превращает массив в указатель типаint *, и т.д.
в памяти выделяется две ячейки, каждая из которых занимает
sizeof(int) * 3
Я надеюсь вы понимаете, что элементы массива идут в памяти подряд, поэтому вместо "две ячейки по sizeof(int)*3" можно было бы с тем же успехом сказать "6 ячеек по sizeof(int)". Это просто 6 int-ов подряд.