为什么会产生分段错误?

#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


arrarr + 1指向20个整数的数组。

arr + 0 – > int int int … int(20 ints,contious)
[0] [0] [0] [1]
arr + 1 – > int int int … int(20 int,con)
[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]做必要的指针运算。

问题是对于2d数组的int arr[20][20]意味着该数组存储为1d数组,并且行一个接一个地存储。 当您对int **arr进行索引时,实际上从数组的第一行获取第二个元素,然后取消引用它并在那里取第一个元素。