У меня есть функция со следующей подписью:
void box_sort(int**, int, int)
и переменная следующего типа:
int boxes[MAX_BOXES][MAX_DIMENSIONALITY+1]
Когда я вызываю функцию
box_sort(boxes, a, b)
GCC дает мне два предупреждения:
103.c:79: warning: passing argument 1 of ‘box_sort’ from incompatible pointer type (string where i am calling the function)
103.c:42: note: expected ‘int **’ but argument is of type ‘int (*)[11] (string where the function is defined)
Вопрос состоит в том почему? Не являются ли интервал x [] [] и интервал ** x (и на самом деле интервал* x []) теми же типами в C?
Я знаю, что пару дней назад был почти такой же вопрос ... хотя сейчас не могу его найти.
Ответ таков: int [size] []
(см. Примечание внизу) и int **
определенно не одного типа. Вы можете использовать int []
и int *
взаимозаменяемо во многих случаях, в частности в таких случаях, потому что массив распадается на указатель на первый элемент, когда вы передаете его в функцию . Но для двумерного массива это очень разные методы хранения.
Вот как они будут выглядеть в памяти для массива 2x2:
int a[2][2]:
__a[0][0]__|__a[0][1]__|__a[1][0]__|__a[1][1]__
(int) (int) (int) (int)
int **a (e.g. dynamically allocated with nested mallocs)
__a__
(int**)
|
v
__a[0]__|__a[1]__
(int*) (int*)
| |
| |
v ------------------>
__a[0][0]__|__a[0][1]__ __a[1][0]__|__a[1][1]__
(int) (int) (int) (int)
Второй массив можно построить следующим образом:
int **a = malloc(2 * sizeof(int*));
a[0] = malloc(2 * sizeof(int));
a[1] = malloc(2 * sizeof(int));
Примечание: как уже отмечали другие, int [] []
не настоящий тип; может быть не указан только один из размеров. Но суть вопроса здесь в том, являются ли двумерный массив и двойной указатель одним и тем же.
Когда выражение массива появляется в большинстве контекстов, его тип неявно преобразуется из «N-элементного массива T» в «указатель на T», а его value устанавливается равным адресу первого элемента в массиве. Исключениями из этого правила являются случаи, когда выражение массива является операндом операторов sizeof
или адреса ( и
), или если выражение массива является строковым литералом, используемым для инициализации другой массив в объявлении.
В контексте кода это означает, что при вызове box_sort
тип выражения box
неявно преобразуется из массива M-элементов N-элементный массив от int
до указатель на N-элементный массив int
или int (*) [MAX_DIMENSIONALITY + 1]
, поэтому ваша функция должна ожидать типы параметров например:
void box_sort(int (*arr)[MAX_DIMENSIONALITY+1], int x, int y)
{
...
}
Поскольку int * a
и int a []
являются синонимами в объявлении параметра функции, отсюда следует, что int (* a) [N]
является синонимом int a [] [N]
, поэтому вы можете записать приведенное выше как
void box_sort(int arr[][MAX_DIMENSIONALITY+1], int x, int y)
{
}
, хотя я лично предпочитаю обозначение указателя, поскольку оно более точно отражает то, что происходит. Обратите внимание, что в вашей функции вы должны использовать индекс arr
как обычно:
arr[x][y] = ...;
, поскольку выражение arr [x]
эквивалентно * (arr + x)
, указатель неявно разыменовывается.
Если вы хотите, чтобы box_sort работал с массивами произвольного размера (т. Е., массивы, в которых второе измерение не обязательно MAX_DIMENSIONALITY + 1), то один из подходов состоит в следующем:
int boxes[X][Y];
...
box_sort (&boxes[0], X, Y, x, y);
...
void box_sort(int *arr, size_t rows, size_t cols, int x, int y)
{
...
arr[x*cols + y] = ...;
}
По сути, вы обрабатываете боксы
как одномерный массив int и вычисляете смещения вручную.
Вы никогда не создавали массив указателей, как того требует подпись.
Есть два способа создавать 2D-массивы на C. В одном случае у вас просто много чего-то, и компилятору сообщают, каковы размеры. Он вычисляет начало строки, умножая индекс строки на количество столбцов, а затем добавляет индекс столбца, чтобы найти элемент в этой строке.
Другой способ - использовать вектор указателей, когда компилятор просто разыменовывает вектор, чтобы найти начало строки, но компилятор не сделает это за вас автоматически, вы должны сделать это сами.
Ваш реальный объект относится к первому типу, но ваш прототип функции требует второго типа.
Таким образом, вы должны либо изменить прототип, чтобы он соответствовал объекту, либо построить вектор указателей строк для передачи функции.
В Си нет такого типа как int[][][]
, только первая часть многомерного массива может быть неопределенной. Так что int[][5]
- это нормально.
В дополнение к другим ответам, опубликованным здесь, если вы можете использовать C99, вы можете использовать массивы переменных для достижения того, что вы хотите:
void box_sort(int N, int M, int x[M][N]);
Это будет работать на большинстве платформ, кроме Visual C++ от Microsoft.