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

use*_*768 11 c scope

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

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

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

use*_*109 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;
}
Run Code Online (Sandbox Code Playgroud)

在此代码中,goto执行时不会退出块.但是,n变化的价值,所以array需要分配新的.这是规范试图支持的非常令人费解的情况.

  • 相反,@ JacekCz,很可能堆栈空间不是为VLA保留,直到机器代码中与其声明相对应的一个点,因为它的大小可能取决于同一块中该声明之前的代码.当VLA超出范围时,该空间确实被解除分配也是合理的,因为它在重新分配时可能具有不同的大小. (2认同)

Joh*_*ger 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
}
Run Code Online (Sandbox Code Playgroud)

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