当变量超出范围时会发生什么?

在大多数托管语言(即具有GC的语言)中,超出范围的局部变量不可访问且具有更高的GC优先级(因此,它们将首先被释放)。

现在,C不是托管语言,超出范围的变量会发生什么?

我在C中创建了一个小测试用例:

#include  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 ,为什么? 何时/在什么情况下可以释放局部变量?

代码示例的实际行为由两个主要因素决定: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通常会为两个指针输出相同的指针值。

自动对象的生命周期在声明它的块的末尾结束。

在C生命周期之外访问对象是C中未定义的行为。

(C99,6.2.4p2)“如果一个对象在其生命周期之外被引用,则该行为是未定义的。当指向的对象到达其生命周期的末尾时,指针的值变得不确定。”

局部变量在堆栈上分配。 在您考虑GC语言或堆上分配的内存的意义上,它们不会“释放”。 它们只是超出范围,对于内置类型,代码不会做任何事情 – 对于对象,析构函数被调用。

超出其范围访问它们是未定义的行为。 你很幸运,因为没有其他代码覆盖了那个内存区域……