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

C 当变量超出范围时会发生什么?,c,memory-management,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

在大多数托管语言(即带有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;
}
#包括
内部主(空){
int*ptr;
{
//新范围
int tmp=17;
ptr=&tmp;//看看内存是否被清除
}
//printf(“tmp=%d”,tmp);//编译时错误(如预期)
printf(“ptr=%d\n”,*ptr);
返回0;
}

我使用GCC4.7.3进行编译,上面的程序会打印
17
,为什么?什么时候/在什么情况下释放局部变量?

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

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

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


局部变量在堆栈上分配。它们并没有像您想象的GC语言或堆上分配的内存那样被“释放”。它们只是超出了范围,对于内置类型,代码不会做任何事情——对于对象,调用析构函数


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

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

截至今天,
gcc
4.7.3
尚未发布。这必须是
4.7.3
预发布版本。