假设我们有以下代码:
void foo() {
char buffer[100];
}
在 foo() 返回之前,C 中是否有一种(最好是可移植的)方法从运行时堆栈中释放缓冲区(类似于添加 esp,汇编中的 100) ?
假设我们有以下代码:
void foo() {
char buffer[100];
}
在 foo() 返回之前,C 中是否有一种(最好是可移植的)方法从运行时堆栈中释放缓冲区(类似于添加 esp,汇编中的 100) ?
不,你可以在 C 中做的最好的事情是使用作用域:
void foo()
{
{
char buffer[100];
}
}
并依靠编译器buffer
在内部范围退出后再次考虑可用的 100 个字节。不幸的是,标准不能保证这一点,您需要依赖编译器。例如,考虑在堆栈空间为 8192KB ( ulimit -s
) 的典型 Linux 机器上的以下程序:
#include <stdio.h>
int main(void)
{
{
char buffer1[8192 * 800] = { 0 };
((char volatile *)buffer1)[0] = buffer1[0];
printf("%p\n", buffer1);
}
{
char buffer2[8192 * 800] = { 0 };
((char volatile *)buffer2)[0] = buffer2[0];
printf("%p\n", buffer2);
}
return 0;
}
(奇怪的演员表是为了防止缓冲区变量被优化出来。)
当不使用优化构建时,该程序将溢出某些编译器上的可用堆栈空间。clang -O0
例如会崩溃,但clang -O1
会重用buffer1
内存,buffer2
并且两个地址将相同。换句话说,buffer1
当范围退出时,在某种意义上已经“释放”了。
另一方面,GCC 甚至会在-O0
.
鉴于:
void foo(void) {
char buffer[100];
}
对象的生命周期buffer
从执行到达开始处{
(进入函数时)开始,到执行离开块到达结束处}
(或通过其他方式,例如goto
、break
或return
语句)结束。
buffer
除了离开块之外,没有其他方法可以结束生命周期。如果您希望能够释放它,您必须以其他方式分配它。例如,如果你通过调用来分配一个对象malloc()
,你可以(并且几乎必须)通过调用来释放它free
:
void foo(void) {
char *buffer_ptr = malloc(100);
if (buffer_ptr == NULL) /* error handling code here */
/* ... */
if (we_no_longer_need_the_buffer) {
free(buffer_ptr);
}
/* now buffer_ptr is a dangling pointer */
}
buffer
另一种方法是通过在嵌套块中定义它来限制它的生命周期:
void foo(void) {
/* ... */
{
char buffer[100];
/* ... */
}
/* buffer's lifetime has ended */
}
但仅仅因为buffer
当控制离开内部块时,它的生命周期结束,这并不能保证它在物理上被释放。在抽象机中,buffer
离开内部块后不再存在,但生成的代码可能会因为更方便而将其留在堆栈中。
如果你想控制分配和释放,你需要使用malloc
and free
。
它是自动变量,您不必做任何事情,因为它会在离开它的范围时重新分配。在您从函数返回时
如果您想缩小范围,只需将其放在另一个范围内。
foo()
{
for(... .)
{
// if defined here it will be only in the for loop scope
}
{
// if defined here it will be only in this internal scope
}
}
请记住,它需要允许它的 C 标准。
因为char buffer[100];
被声明为函数堆栈的本地,所以void foo()
当void foo()
返回时,存储空间buffer
被释放以供重用,并且在该点之后不再有效访问。
因此,您无需或可以做任何事情来“释放缓冲区”,因为它是一个具有自动存储持续时间buffer
的数组,可以自动处理,例如:C11 Standard - 6.2.4 Storage duration of objects (p5)