Двумерный массив, указатели и вывод значений элементов массива в c++

Объясните, пожалуйста, почему вот в этом коде

#include <bits/stdc++.h>
using namespace std;
int main() 
{
    int arr[][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    for (int i = 0; i < 9; i++) 
    {
        cout << arr[i] << '\t' << sizeof(arr[i]) << '\n';
    }
}

выводятся следующие значения:

0x61fdf0        12
0x61fdfc        12
0x61fe08        12
0x61fe14        12
0x61fe20        12
0x61fe2c        12
0x61fe38        12
0x61fe44        12
0x61fe50        12

То есть почему размер адресов переменных двумерного массива равен 12 байтам, при этом разница между соседними адресами, например, 0x61fe14 и 0x61fe20 составляет 6 байтов? Откуда вообще взялись эти числа, если целые числа занимают 4 байта.

P.S. И почему вообще, если вывести arr[i] выведется именно адрес переменной? То есть, в случае одномерного массива, если есть переменная arr[i], то ее адрес - &arr[i]. Почему нельзя было адрес переменной выводить только как &arr[i][j], зачем вторая интерпретация? Заранее спасибо.


Ответы (1 шт):

Автор решения: wololo

Запись

int arr[][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

эквивалентна записи

int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

Следовательно, arr есть одномерный массив из трёх элементов. А элементы массива arr сами являются одномерными массивами из трёх элементов типа int.

Таким образом, имеем следующий расклад:
тип arr — это int[3][3],
тип arr[i] — это int[3],
тип arr[i][j] — это int.

sizeof(arr) == 3 * sizeof(int[3]) == 3 * (3 * sizeof(int)) // == 3 * (3 * 4), если sizeof(int) == 4

То есть почему размер адресов переменных двумерного массива равен 12 байтам

Потому что arr[i] — это не адрес переменной двумерного массива, а одномерный массив из трёх элементов типа int. Оператор sizeof применённый к массиву, возвращает количество байтов, занимаемых массивом.

И почему вообще, если вывести arr[i] выведется именно адрес переменной?

Потому что нет перегруженного оператора << для вывода массива интов, но есть оператор для вывода указателей const void*. А массив может быть неявно преобразован в указатель на свой первый элемент, а указатель int* может быть неявно преобразован в const void*.

Обратите внимание, что если массив arr[i] преобразуется в указатель, то это указатель на инт: int*. В то время как адрес самого массива &arr[i] имеет тип указателя на массив из трёх элементов типа инт: int(*)[3].

при этом разница между соседними адресами, например, 0x61fe14 и 0x61fe20 составляет 6 байтов?

Использование букв в качестве цифр в записи числа вас не смутило? ;) Префикс 0x сигнализирует о том, что числа записаны в шестнадцатеричной системе счисления. Более наглядно разницу вы увидите, посчитав разность между вторым и первым адресами:

0x61fdfc - 0x61fdf0 == 0xC == 12 (в десятичной) == sizeof(int[3]) == 3 * sizeof(int)

Наконец, так как в массиве arr всего три элемента, то и цикл должен быть от нуля до трёх:

for (int i = 0; i < 3; i++)

Если хотите вывести самые вложенные int'ы используете два цикла:

for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < 3; j++)
        cout << arr[i][j] << " ";
    cout << "\n";
}
→ Ссылка