为什么会产生分段错误?
#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,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
进行索引时,实际上从数组的第一行获取第二个元素,然后取消引用它并在那里取第一个元素。