C 可变长度数组存储持续时间
Posted
技术标签:
【中文标题】C 可变长度数组存储持续时间【英文标题】:C Variable Length Arrays storage duration 【发布时间】:2018-06-27 22:27:58 【问题描述】:在this 网站上有以下段落(强调我的):
自动存储持续时间。当进入声明对象的块时分配存储,并在以任何方式退出时(转到、返回、到达末尾)释放存储。一个例外是 VLA。它们的存储是在声明执行时分配的,而不是在块入口时分配的,并且在声明超出范围时被释放,而不是在块退出时(C99 起)。如果以递归方式进入块,则为每个递归级别执行新的分配。所有函数参数和非静态块作用域对象都具有此存储持续时间,以及在块作用域中使用的复合字面量。
声明超出范围和退出块有什么区别?可以举个例子吗?
【问题讨论】:
en.cppreference.com/w/c/language/array 请参阅有关可变长度数组的部分。它有一个很好的例子。 @DeiDei 我读了那个例子,它没有具体说明两者之间的区别。我只能认为在块的末尾有一条特殊指令,负责为 VLA 取消分配内存。因此,如果使用return
或goto
退出块,则不会释放内存。这是正确的吗?
基本上这是所有意大利面条编码员的注意事项。如果你写的是理智的代码,你就不需要关心这个。
【参考方案1】:
来自 C11 规范 §6.2.4/7 的 N1570 草案
对于这样一个具有可变长度数组类型的对象,它的 生命周期从对象的声明到执行 程序离开了声明的范围。
该规范随后添加了这个有用的注释:
离开包含声明的最里面的块,或跳转到 该块中的一个点或声明之前的嵌入块, 离开声明的范围。
因此,当执行超出 VLA 范围时,VLA 将被解除分配,其中包括 VLA 声明之前同一块中的部分。
可以使用goto
语句跳转到声明之前的某个点。例如:
int n = 0;
while (n < 5)
top:
n++;
char array[n];
if (n < 2)
goto top;
在此代码中,执行goto
时不会退出块。但是,n
的值发生了变化,因此需要分配一个新的array
。这就是规范试图支持的极其复杂的情况。
【讨论】:
谢谢。因此,当执行超出 VLA 范围时,VLA 将被解除分配,其中包括 VLA 声明之前同一块中的部分。 @user42768 是的,这是一个很好的表达方式。如果我将该评论复制到答案中可以吗? user3386109 当然,任何能让未来的读者更好地理解这一点的东西。 假设正式定义使用不同的词,实际机器代码不同。函数返回后,栈上的 Belive 帧递减(即内部块中的变量在高级语言中没有意义,但栈没有中化) 相反,@JacekCz,很可能直到机器代码中与其声明相对应的某个点才为 VLA 保留堆栈空间,因为它的大小可能取决于该声明之前的代码在同一个街区。当 VLA 超出范围时,该空间确实被释放也是合理的,因为它在重新分配时可能具有不同的大小。【参考方案2】:声明超出范围和退出块有什么区别?可以举个例子吗?
块范围标识符的范围从它的声明开始,一直延伸到最里面的封闭块的末尾。这适用于各种标识符,而不仅仅是 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 仅在其标识符范围内有效。
【讨论】:
谢谢。我赞成你的回答,但我只能接受一个,@user3386109 的回答是第一位的。以上是关于C 可变长度数组存储持续时间的主要内容,如果未能解决你的问题,请参考以下文章