我正在尝试得到指针的深度确认。当我运行这个例子时,由于分段错误,它没有输出任何东西。但是当我试图从GDB逐行运行代码时,它工作正常。
#include <stdio.h>
struct s{
int a;
struct s* next;
};
struct s foo() {
struct s m ;
struct s f[10];
m.a = 55;
m.next =&(f[0]);
int i = 0;
while(i < 9) {
f[i].a = 28 + i;
f[i].next = &(f[i+1]);
i++;
}
return f[0];
}
int main()
{
struct s f = (foo());
printf("%d ",f.a);
printf("%d ",f.next->a);
printf("%d ",f.next->next->a);
return 0;
}
如果你把main函数改成这样:
int main(){
struct s f = (foo()); int a = f.a;
int b - f.next->a;
int c = f.next->next->a;
int d = f.next->next->next->a;
int g = f.next->next->next->next->a;
printf("%d %d %d %d %d\n", a, b , c ,d, g);
}
它将工作正常.当我尝试直接调用printf时,这个屏幕截图不起作用.
当我尝试先在变量中存储,然后在变量
上调用printf时,这是有效的]
1条答案
按热度按时间ndasle7k1#
在C中,在函数内部定义的变量可以是静态的,也可以是自动的(auto)。默认值是自动的。当函数返回并且其返回值已复制到另一个变量或已在表达式中使用时,自动变量的生存期结束。
因此,在
foo
的返回值存储在main
的f
中之后,foo
的f
数组不再是活动的,尝试访问它的任何部分都是错误的。那么为什么下面的方法有效呢?
原因是你的C实现将自动变量存储在堆栈中,并且在一个函数完成后不收缩堆栈。相反,变量保持不变,直到另一个函数被调用。那个函数将重用全部或部分堆栈空间,覆盖变量而不考虑它们的旧类型。指针可能被一个整数、字符串的几个字符或其他东西覆盖。
因此,尽管这是未定义的行为,并且您永远不应该依赖于此工作,但在此特定情况下,您可以访问
f
数组的一部分,因为自foo
返回以来,没有调用任何函数。类似地,在GDB会话中,由于您在
foo
返回之后但在调用 printf 之前停止了程序,因此GDB可以安全地访问f.next->next->a
等,但这仍然是未定义的行为。你的第二个版本 * 不 * 调用函数,这就是你会遇到麻烦的时候。
第一行
printf("%d ",f.a)
可以正常工作。main
的f
是有效的。但是**对 printf 的调用将覆盖foo
**以前使用的堆栈,包括foo
的f
数组的全部或部分。注意,您还看不到这个输出,因为在您要求 printf 打印行尾字符之前,它不会打印任何内容。
下一行
printf("%d ",f.next->a);
将打印一个看似随机的整数,因为foo
的f[1].a
已经被 printf 使用的局部变量覆盖了,而且,同样,由于缓冲的原因,您还看不到任何输出。下一行,
printf("%d ",f.next->next->a)
,是事情崩溃的地方。foo
的f[1].next
几乎肯定不会是一个有效的指针,所以你会得到一个分段错误。你怎样才能让它正常工作呢?如果你想让
foo
的f
数组在整个程序运行期间保持活跃,只需要在声明前加上关键字static
。这意味着f
数组将有一个副本,不管你调用foo
多少次。如果你想每次调用foo
都分配一个新的f
数组,你可以使用 malloc 函数。2由 malloc 分配的空间将保持活动状态,直到你调用 free 或者程序退出。