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

时间:2012-12-15 00:53:11

标签: c memory-management

在大多数托管语言(即具有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,为什么?何时/在什么情况下会释放局部变量?

3 个答案:

答案 0 :(得分:16)

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

答案 1 :(得分:5)

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

在C。

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

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

答案 2 :(得分:3)

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

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