Почему нужно указывать количество столбцов массива?
Есть массив:
int a[5][5];
И есть функция:
int foo(int d[][5])
{
return 1;
}
Почему нельзя передать в функцию двумерный массив как указатель на указатель? И почему нужно обязательно указывать количество столбцов?т.е int d[ ][ ] нельзя так передать?
Ответы (3 шт):
Потому что указатель на указатель - это совсем другая структура (другое размещение элементов в памяти). Это массив указателей, где каждый указывает на еще один одномерный массив где-то в другом месте памяти.
Двумерный же массив - это единый блок памяти, и компилятор должен знать, где заканчивается одна строка и начинается вторая. И если это ему не указать, как вы предлагаете - то как найти нужное место в этом блоке?...
Массив 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> или по ссылке.
Во-первых, массив - это не указатель, массив - это массив. Хотя он может в некоторых ситуациях неявно преобразовываться к указателю на свой первый элемент.
Во-вторых, 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
}