局部变量的安全性

Safety of Local Variable

本文关键字:安全性 局部变量      更新时间:2023-10-16

以下函数是否安全,以返回指向局部变量的指针?

int * foo(void)
{
int a = 6;
int *p = &a;
return p;
}

如果没有,在什么条件下?如果是,编译器如何保证安全性?

尝试的测试案例:

int * foo(void)
{
int a = 6;
int *p = &a;
return p;
}
int * bar(void)
{
int b = 7;
int *p = &b;
return p;
}
int main()
{
int a = *foo();
int b = *bar();
printf("%d, %d, %dn", 1, 2, 3); //to mess up stack
printf("%d, %dn", a, b);
return 0;
}

它将成功打印"6,7"。然而,使用-O2,它会打印"0,0">

这样做的结果将是未定义的行为,这肯定是不安全的。

a是函数(即automatic variable)的局部变量,因此它被存储在堆栈中。当函数返回堆栈帧时,向上(或向下)移动,留下的内存可能会被下一个调用的函数覆盖,因此指针将指向内存中的相同方向,但可以是任何随机字节。

简而言之,它从来都不安全,因为分配给该变量的内存迟早会被重用。

它适用于您的示例,因为它们很简单,在返回指针和访问指针所指向的内存之间,堆栈上没有发生太多其他事情。

只有当您返回指针的内存被显式地分配了malloc或类似的东西时,它才是安全的。

此链接可以更好、更详细地解释这一点:http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Mips/stack.html

这个函数永远都不安全。有时可能会意外地做你所期望的事情,但仅此而已。

稍微不那么简洁:返回指向局部变量的指针总是会引发未定义的行为。未定义的行为包括这次不会发生任何错误的可能性,而通常基于堆栈的C实现允许您在大多数情况下将指针解引用到堆栈顶部正上方的内存中。main在函数返回后立即取消引用这些指针,这使得它更有可能"工作"(特别是,您的"弄乱堆栈"printf不会影响任何事情,因为此时无效指针已经失效)。然而,将int a = *foo()安排为segfault的实现将是一致的。

有趣的事实:C99的文本中没有出现"堆栈"一词。所有实现都需要支持递归函数调用,通常的方法是使用堆栈,但堆栈并不是唯一的方法。例如,Cheney在M.T.a.中,底层的C堆栈被重新用作垃圾收集器托儿所,所有的延续都是显式的——C实现和Scheme实现一样可以做到这一点。