Почему это порождает ошибку сегментации?

#include void foo(int **arr) { arr[1][1]++; } main() { int arr[20][20]; printf("%d\n",arr[1][1]); foo((int**)arr); printf("%d\n",arr[1][1]); } 

Предположим, вы заявляете: int arr [10] [20];
Какой тип arr?
Вы можете подумать, что это int ** , но это неверно.

Его действительно тип int (*)[20] когда он распадается (например, когда вы передаете его функции);
Затухание массивов применяется только один раз.

Подробнее здесь


Теперь рассмотрим следующее:

 #include #include void foo(int arr[][20]) { arr[1][1]++; } main() { int (*arr)[20]; arr = malloc(sizeof(int (*)[]) * 2); //2 rows & malloc will do implicit cast. printf("%d\n",arr[1][1]); foo(arr); printf("%d\n",arr[1][1]); } 

Выход :

$ gcc fdsf.c && ./a.out
0
1


arr и arr + 1 указывают на массив из 20 целых чисел.

arr + 0 -> int int int … int (20 ints, смежный)
[0] [0] [0] [1]
arr + 1 -> int int int … int (20 ints, смежный)
[1] [0] [1] [1]

Вот как выглядит int[2][2] в памяти:

int[2] int[2]

То есть массив сразу же следует за другим массивом.

Вот как выглядит int[2] в памяти:

int int

То есть int сразу следует за другим int.

Итак, вот как выглядит int[2][2] в памяти:

 int int int int ^ ^ | |___ this is arr[1][1] | |____ this is p[1], assuming sizeof(int*) == sizeof(int) 

Если вы приложите arr к int** , я назову результат p . Затем он указывает на ту же память. Когда вы выполняете p[1][1] вы не получаете arr[1][1] . То, что делает программа вместо этого, оно считывает значение в p[1] , корректирует это значение по размеру int и разыскивает его . Если этот второй int содержит, скажем, значение «21», то вы просто попытались разыменовать указатель «25» (если int – 4 байта). Это неправильно.

Массивы не совпадают с указателями, а 2-D массивы – это, конечно, не то же самое, что указатели на указатели.

Поскольку foo ожидает указатель на указатель на int, и вы передаете ему указатель на массив из 20 int. Кастинг не изменит того факта, что это не правильный тип.

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

 #include void foo(int arr[][20]) { arr[1][1]++; } int main() { int arr[20][20]; arr[1][1] = 1; printf("%d\n",arr[1][1]); foo(arr); printf("%d\n",arr[1][1]); } 

foo должен знать размер массива (ну, по крайней мере, второе измерение массива, сначала не нужно), в противном случае он не может выполнить необходимую арифметику указателя для [1][1] .

Проблема в том, что int arr[20][20] для массива 2d означает, что этот массив хранится как 1d-массив, а строки хранятся один за другим. когда вы индексируете int **arr вы фактически берете 2-й элемент из первой строки массива, затем вы разыгрываете его и берете первый элемент там.