Почему нужно указывать количество столбцов массива?

Есть массив:

int a[5][5];

И есть функция:

int foo(int d[][5])
{
    return 1;
}

Почему нельзя передать в функцию двумерный массив как указатель на указатель? И почему нужно обязательно указывать количество столбцов?т.е int d[ ][ ] нельзя так передать?


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

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

Потому что указатель на указатель - это совсем другая структура (другое размещение элементов в памяти). Это массив указателей, где каждый указывает на еще один одномерный массив где-то в другом месте памяти.

Двумерный же массив - это единый блок памяти, и компилятор должен знать, где заканчивается одна строка и начинается вторая. И если это ему не указать, как вы предлагаете - то как найти нужное место в этом блоке?...

→ Ссылка
Автор решения: user7860670

Массив int a[5][5]; может неявно преобразовываться к указателю на первый элемент, т.е. к указателю на массив из 5 int, что и происходит при передаче его в функцию. Однако нет никакого контекста, при котором он мог бы преобразоваться к указателю на указатель. Для такого преобразования требуется чтобы элементы массива были указателями, т.е. чтобы изначально был int * a[5];.

Аргумент вида int d[][] вызовет ошибку, так как в С++ нельзя объявлять массивы из незавершенных (incomplete) типов, к которым относится любой массив неизвестной длины.

Скорее всего тут, как в 80% случаев, использование массива массивов не нужно. И вместо int a[5][5]; лучше использовать ::std::array<int, 5 * 5> a; или vector, а вместо указателя int d[][5] передавать ::std::span<int, 5 * 5> или по ссылке.

→ Ссылка
Автор решения: wololo

Во-первых, массив - это не указатель, массив - это массив. Хотя он может в некоторых ситуациях неявно преобразовываться к указателю на свой первый элемент.

Во-вторых, n-мерный массив, n > 1, на самом деле является одномерным массивом, элементами которого являются n-1-мерные массивы.

int arr[2][5]={{0, 1, 2, 3, 4}, {5, 6, 7, 8, 9}};//Массив из двух элементов типа (массив из пяти элементов типа int)

int (*p1)[2][5]; //Указатель на массив из двух элементов типа (массив из пяти элементов типа int)
int (*p2)[5]; //Указатель на массив из пяти элементов типа int
int* p3; //Указатель на int

p1 = &arr; //p1 указывает на массив arr;
p2 = &arr[0]; //p2 указывает на первый элемент массива arr, т.е. на массив из пяти элементов типа int
p3 = &arr[0][0]; //p3 указывает на первый элемент первого подмассива массива arr

cout << sizeof(arr) << " " << sizeof(*p1) << endl; //2 * 5 * sizeof(int)
cout << sizeof(arr[0]) << " " << sizeof(*p2) << endl; //5 * sizeof(int)
cout << sizeof(arr[0][0]) << " " << sizeof(*p3) << endl; //sizeof(int)

cout << sizeof(p1) << " " << sizeof(p2) << " " << sizeof(p3) << endl; //Вывод зависит от реализации. Как вариант, "8 8 8" или "4 4 4".

//Массив может быть неявно преобразован к указателю на свой первый элемент:
arr + 1; //Указатель на второй подмассив массива arr
*(arr + 1) + 4; //Указатель на 5 элемент второго подмассива массива arr
cout << *(*(arr + 1) + 4) << endl; //9

В-третьих, массив не может быть параметром функции. Всё что похоже на массив в параметре функции на самом деле интерпретируется как указатель на элемент массива.

void f(int* arr0, int arr1[], int arr2[5], int arr3[2][5], int (* arr4)[2][5])
{
    //arr0, arr1 и arr2 имеют одинаковый тип - указатель на int
    //arr3 - указатель на массив из пяти элементов типа int.
    //arr4 - указатель на массив из двух элементов типа (массив из пяти элементов типа int)
    cout << 
        is_same<decltype(arr0), int *>::value <<  //1
        is_same<decltype(arr1), int *>::value <<  //1
        is_same<decltype(arr2), int *>::value <<  //1
        is_same<decltype(arr3), int (*)[5]>::value <<  //1
        is_same<decltype(arr4), int (*)[2][5]>::value << endl;  //1
}
→ Ссылка