Несовместимый тип указателя

у меня есть функция со следующей подписью:

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)

вопрос почему? Являются ли int x[][] и int** x (и фактически int* x[]) не одинаковыми типами в C?

4 ответов


Я знаю, что пару дней назад был почти точно такой же вопрос... но сейчас не могу его найти.

ответ: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[][] не является реальным типом; только один из размеров может быть не указан. Но суть вопроса здесь в том, являются ли двумерный массив и двойной указатель одним и тем же.


вы никогда не строили массив указателей, как требует подпись.

есть два способа сделать 2D-массивы в C. В одном случае у вас просто много чего-то, и компилятору говорят, каковы размеры. Он вычисляет начало строки умножением строк на количество столбцов, а затем он добавляет индекс столбца, чтобы найти элемент в этой строке.

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

ваш фактический объект является одним из первых, но ваш прототип функции запрашивает второй вид.

таким образом, вы должны либо изменить прототип, чтобы соответствовать объекту, либо построить вектор указателей строк для передачи функции.


В C нет такого типа, как int[][], только первая часть многомерного массива может быть определен. Так что int[][5] - это хорошо.

В дополнение к другим ответам, размещенным здесь, Если вы можете использовать C99, вы можете использовать массивы переменных для выполнения того, что вы хотите:

void box_sort(int N, int M, int x[M][N]);

Это будет работать на большинстве платформ, кроме Microsoft Visual C++.


когда выражение массива появляется в большинстве контекстов, его тип неявно преобразуется из" N-элементного массива T "в" указатель на T", а его значение устанавливается в адрес первого элемента массива. Исключения из этого правила, когда выражение массива является операндом sizeof или адреса (&) операторы, или если выражение массива является строковым литералом, используемым для инициализации другого массива в объявлении.

что это означает в контексте ваш код - это ваш вызов box_sort, тип выражения boxes неявно преобразуется из M-element array of N-element array of int to pointer to N-element array of 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] = ...;
}

в основном, вы лечите boxes как 1-d массив int и вычисление смещений вручную.