当变量超出范围时会发生啥?
Posted
技术标签:
【中文标题】当变量超出范围时会发生啥?【英文标题】:What happens when a variable goes out of scope?当变量超出范围时会发生什么? 【发布时间】:2012-12-03 00:29:35 【问题描述】:在大多数托管语言(即具有 GC 的语言)中,超出范围的局部变量是不可访问的,并且具有更高的 GC 优先级(因此,它们将首先被释放)。
现在,C 不是托管语言,这里超出范围的变量会发生什么?
我用 C 创建了一个小测试用例:
#include <stdio.h>
int main(void)
int *ptr;
// New scope
int tmp = 17;
ptr = &tmp; // Just to see if the memory is cleared
//printf("tmp = %d", tmp); // Compile-time error (as expected)
printf("ptr = %d\n", *ptr);
return 0;
我使用 GCC 4.7.3 编译,上面的程序打印出17
,为什么?以及何时/在什么情况下会释放局部变量?
【问题讨论】:
gcc
4.7.3
截至今天尚未发布。这必须是 4.7.3
预发布版本。
【参考方案1】:
代码示例的实际行为由两个主要因素决定:1) 行为是由语言未定义,2) 优化编译器将生成与 C 语言物理不匹配的机器代码代码。
例如,尽管行为未定义,但 GCC 可以(并且将)轻松地将您的代码优化为仅仅
printf("ptr = %d\n", 17);
这意味着您看到的输出与代码中的任何变量发生的情况几乎没有关系。
如果您希望代码的行为更好地反映物理上发生的情况,您应该声明您的指针volatile
。行为仍然是未定义的,但至少会限制一些优化。
现在,关于局部变量超出范围时会发生什么。没有任何物理发生。一个典型的实现会在程序堆栈中分配足够的空间来存储当前函数中最深的块嵌套级别的所有变量。该空间通常在函数启动时一次性分配到堆栈中,并在函数退出时释放。
这意味着以前由tmp
占用的内存继续保留在堆栈中,直到函数退出。这也意味着相同的堆栈空间可以(并且将)被同级块中具有大致相同“局部深度”级别的不同变量重用。该空间将保存最后一个变量的值,直到在某个兄弟块变量中声明的其他变量覆盖它为止。在您的示例中,没有人会覆盖以前由 tmp
占用的空间,因此您通常会看到值 17
在该内存中完好无损。
但是,如果你这样做
int main(void)
volatile int *ptr;
volatile int *ptrd;
// Block
int tmp = 17;
ptr = &tmp; // Just to see if the memory is cleared
// Sibling block
int d = 5;
ptrd = &d;
printf("ptr = %d %d\n", *ptr, *ptrd);
printf("%p %p\n", ptr, ptrd);
您将看到以前由tmp
占用的空间已被d
重用,并且其以前的值已被覆盖。第二个printf
通常会为两个指针输出相同的指针值。
【讨论】:
【参考方案2】:局部变量在栈上分配。在您考虑 GC 语言或在堆上分配的内存的意义上,它们并没有“释放”。它们只是超出范围,对于内置类型,代码不会做任何事情 - 而对于对象,则调用析构函数。
超出范围访问它们是未定义的行为。你很幸运,因为还没有其他代码覆盖该内存区域......。
【讨论】:
【参考方案3】:自动对象的生命周期在它被声明的块的末尾结束。
在对象的生命周期之外访问对象是 C 中未定义的行为。
(C99, 6.2.4p2) “如果对象在其生命周期之外被引用,则行为未定义。当指针指向的对象到达其生命周期的末尾时,指针的值变得不确定。”
【讨论】:
以上是关于当变量超出范围时会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章