我有一个简单的 C 代码:
int main()
{
int test[10] = {1,2,3,4,5,6,7,8,9,10};
int **ptr = &test;
printf("%d\n", (*ptr)[0]);
return 0;
}
一旦到达 printf
行,它就会崩溃:
Process returned -1073741819 (0xC0000005) execution time : 0.865 s
在 Ubuntu 上编译代码时,它给我警告:
test.c:8:17: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
int **ptr = &test;
但是,如果我在堆上动态分配内存,则代码可以正常工作:
int main()
{
int *test = malloc(sizeof(int)*10);
for (int i = 0; i < 10; i++) {
test[i]=i;
}
int **ptr = &test;
printf("%d\n", (*ptr)[1]);
return 0;
}
请向我解释一下,为什么当数组在堆上而不是在堆栈上时代码可以工作?
最佳答案
为了让你的 ptr
变量成为“整数数组的地址”,你实际上需要一个更微妙(和神秘)的声明。
这会起作用:
int (*ptr)[10] = &test;
因为它声明,当取消引用时(即当您实际使用表达式*ptr
时),它将是一个数组(包含 10 个元素)。
在某些方面,声明的结构有点类似于 pointers-to-functions 的结构。 .
编辑:在第二种情况下,test
是一个普通的旧指针(通过malloc
调用为其分配了一个值);因此,它本身就是一个变量,其地址可以被获取(正如您的 int **ptr = &test;
行所做的那样 - 正确)。
但是,在第一种情况(“固定”数组)中,test
引用一 block 内存;在许多方面,这可以用作指向第一个元素的指针(例如,就像在函数调用中一样)。但是编译器可以为“第一个元素的地址的地址”分配什么值?这就是在本例中使用 &test
尝试分配失败的原因。
但是,根据上一段,您有权询问编译器如何使用代码确定 &test
的值(分配给 ptr
)在这个答案中给出?好吧,如果您将以下行添加到您的程序中:
printf("%p %p\n", test, &test);
您将看到 test
和 &test
(您可以将 &test
更改为 ptr
- 输出将为相同)具有完全相同的值!因此,使用这个“神秘”声明,您可以为编译器提供足够的信息来了解如何处理“数组指针” - 基本上,它有点“忽略”(或绕过)第一级解引用,最终得到地址数组的第一个元素(按原样)。
关于c - 为什么 C 程序在取消引用数组指针时会崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59923746/