C可变长度阵列存储持续时间

时间:2018-01-18 18:43:58

标签: c scope

this网站上有以下段落(强调我的):

  
      
  • 自动存储时间。在输入声明对象的块时分配存储,并在通过任何方式退出时取消分配(转到,返回,到达结尾)。一个例外是VLA;它们的存储在执行声明时分配,而不是在块条目上分配,并在声明超出范围时解除分配,而不是在退出块时(因为C99)。如果以递归方式输入块,则对每个递归级别执行新的分配。所有函数参数和非静态块作用域对象都具有此存储持续时间,以及块作用域中使用的复合文字。
  •   

超出范围的声明和退出的块之间有什么区别?你能提供一个例子吗?

2 个答案:

答案 0 :(得分:7)

根据C11规范§6.2.4/ 7的N1570草案

  

对于这样一个具有可变长度数组类型的对象,它的   生命周期从对象的声明延伸到执行   该计划离开了声明的范围。

然后规范添加了这个有用的注释:

  

离开包含声明的最里面的块,或跳转到   声明之前该块或嵌入块中的一个点,   留下了宣言的范围。

因此,当执行超出VLA的范围时,VLA将被解除分配,VLA的范围包括声明VLA之前的同一块中的部分。

可以使用goto语句跳转到声明之前的某个点。例如:

int n = 0;
while (n < 5)
{
top:
    n++;
    char array[n];
    if (n < 2)
        goto top;
}

在此代码中,执行goto时不会退出该块。但是,n的值会更改,因此需要分配新的array。这是规范试图支持的非常令人费解的情况。

答案 1 :(得分:6)

  

超出范围的声明和退出的块之间有什么区别?你能提供一个例子吗?

块范围标识符的范围从其声明开始并延伸到最内层封闭块的末尾。这适用于各种标识符,而不仅仅是VLA的标识符。通过转移到标识符声明之前的点,控制可以离开标识符的范围而不退出包含它的最内层块。最明显的方法是通过goto语句:

int f(int n) {
    // i is allocated here, _before_ the beginning of its scope
    label: // if control returns here via the goto below, vla is _de_allocated
           // at this point
    printf("n is %d", n);

    int vla[n]; // vla is (re)allocated here, at the beginning of its scope
    int i;
    int sum;

    for (i = 0; i < n; i++) {
        vla[i] = rand();
        sum += vla[i];
    }

    if (sum < SOME_THRESHOLD) goto label; 

    return sum;  // vla and i are both deallocated when control exits the block
}

解除分配VLA与解除分配普通自动对象的时间之间的差异反映了分配两种类型对象之间的差异。 VLA仅在其标识符范围内存在。