C中函数的局部变量范围
当我开始用C语言编程时,我听说过以下场景。
“ 尝试从外部访问,函数局部变量将导致错误(或垃圾值)。因为当我们从函数返回时堆栈被清除 ”
但我的下面的代码示例打印值为50.我正在使用最新的GCC编译器编译代码。
#include int * left(); int main() { int *p=left(); printf("%d\n",*p); return 0; } int * left() { int i=50; return &i; }
在这个问题上欢迎我。
我能知道C ++中的行为吗? 它类似于c ..
修改它以添加对printf
的第二次调用,您将从第一次看到不同的值。 在启用优化的情况下编译它,您将看到另一组值。 做任何有价值的东西 ,你进入未定义的领域,这意味着编译器可以通过你的鼻段自由地召唤恶魔。
在我的系统上,我看到50
然后是0
; 通过优化,我看到0
然后是32767
。
如果你将局部变量static
,那么你可以返回它的地址,因为它变得像一个全局变量(但请记住它只有一个实例)。
当一个函数返回时,它在堆栈上使用的本地存储现在被程序视为“未使用”,因为堆栈不再那么高了。 但通常情况下,值仍然存在,因为没有迫切需要清除它们。 内存也仍然由程序拥有,因为将内存一次几个字节返回给操作系统是没有意义的。 因此,对于您的具体示例,在您编译它的情况下,指向的内存仍包含值50
。 但是,正式地说, *p
的值是不确定的 ,并且尝试使用它会导致未定义的行为。
C语言的一个存在主义危机是,一方面它没有说明堆栈和构成运行过程的各种hex污泥; 另一方面,有必要了解这些以保护自己免受崩溃,缓冲区溢出和未定义的行为。 请记住,你很幸运GCC会对此发出警告。
变量’i’在堆栈上创建,当函数’left’返回时,堆栈被清除。 当我说清除时,这意味着变量’i’使用的地址被标记为可以重复使用。 直到其他部分代码使用相同的地址,该值将保持不变。 在你的情况下,这是一个幸运的,可以给你想要的结果。 如果你在打电话给’left’之后再调用几个函数,我相当肯定你会得到错误的结果。
根据C标准的行为是未定义的而不是垃圾值。 因此,有可能,偶尔也可能,价值将保持不变。 无论如何都无法保证这一点。
这是运气/机会/事故,没有别的。 不要依赖这种行为,因为它会回来咬你。
看看这个修改过的例子,它清楚地显示了两者之间是否有什么:
int *p=left(); // right here printf("%d\n",*p);
你很容易得到一个损坏的堆栈。 这个想法是你不拥有那个地方,别人可以使用它!
int main() { int *p=left(); right(); // Look here! printf("%d\n",*p); // prints -50 instead of 50 return 0; } ... int * right() { int i=-50; return &i; }
您指的是超出其生命周期的对象( left()
的范围)。 这导致了不确定的行为 – 我猜你仍然得到50,因为没有任何东西覆盖了i
还没有的区域。
它偶然起作用。 当调用printf()
时, p
指向的内存位置仍包含整数值50。 但是调用left()
和printf()
之间的main()
内的任何函数都会覆盖它。
例如,如果您将printf()
调用更改为:我不知道您的实现会发生什么:
printf("abs(%d) = %d ???\n", *p, abs(*p));
不要这样做!
就C标准而言,该值是未定义的。
实际上,只要在引用返回值之前没有任何东西被推送到堆栈,它就可以工作,但是下次调用函数时,返回地址和新堆栈帧被推送到堆栈并可能覆盖引用的值。
这不被认为是可接受的编码(因为未定义的性质)。
未定义的行为。 i
离开left()
被摧毁left()
。 你有一个垃圾的地址在p
。 编译器还没有通过运气摧毁i
。