是否可以在当前函数退出之前显式释放 C 的 alloca() 分配的内存?如果是这样,怎么做?
8 回答
来自http://www.gnu.org/software/libc/manual/html_mono/libc.html#Variable-Size-Automatic:
分配一个块
alloca
是一个显式的动作;您可以根据需要分配任意数量的块,并在运行时计算大小。但是,当您退出调用 alloca 的函数时,所有块都会被释放,就像它们是在该函数中声明的自动变量一样。没有办法明确地释放空间。
这是可能的,但没有预先编写的功能可以做到这一点。您必须深入研究编译器的 alloca() 实现以弄清楚它在做什么,然后编写自己的 freea()。由于每个编译器执行 alloca() 的方式不同,因此您必须为每个编译器重写 freea()。
但我很难相信这值得麻烦。如果您需要显式释放它,只需使用 malloc/free - 这些功能通常都经过大量优化。利用它们。
您正在使用 alloca() 在堆栈上分配;如果之后发生了其他事情(并且如果不将所有内容都写入汇编中就无法控制它),则不能只是将堆栈缩小。因此,除非您离开函数的堆栈框架,否则这是不可能的。
这也是为什么如果您溢出分配的缓冲区,您会真正搞砸事情的原因。你可以开始覆盖你的函数返回的代码地址,导致它跳转到其他地方,各种可怕的东西。当心!
Malloc 在堆上工作,这就是为什么它可以做的事情更加灵活。
使用 C99,您可以使用可变长度数组来实现相同的目的。只需在新范围内声明 VLA;当范围退出时,它将自动释放。
例如:
int some_function(int n) {
// n has the desired length of the array
...
{ // new scope
int arr[n]; // instead of int *arr = alloca(n*sizeof(int));
// do stuff with array
}
// function continues with arr deallocated
...
}
这对于延续传递风格(CPS)很有用,而不是 realloca。
您可以调用一个函数来分配和操作堆栈顶部的字符串,然后将堆栈缩小到字符串的长度并调用下一个函数。
没有概念上的原因为什么不能有一个 freea(),除了堆栈上的最顶层条目之外,它对于任何东西都是 nop。
不,因为它与局部变量一起分配在堆栈上。如果您想要可以显式释放的内存,请使用动态内存分配函数之一。
没有混合允许您显式释放并在函数退出时自动释放它,至少在标准中没有。
是的,但这取决于 alloca() 的实现。alloca() 的一个合理简单的实现是通过调整堆栈指针将新分配的块放在堆栈顶部。因此要释放这块内存我们只需要做一个负分配(但你需要研究alloca()的真正实现),我们以下面的不可移植代码为例来验证它:
#include <stdio.h>
#include <alloca.h>
int main()
{
unsigned long p0, p1, p2;
p0=(unsigned long)alloca(0);
p1=(unsigned long)alloca((size_t) 0x1000);
p2=(unsigned long)alloca((size_t)-0x1000);
printf( "p0=%lX, p1=%lX, p2=%lX\n", p0, p1, p2 );
return 0;
}
在带有 clang 2.9 的旧 x64 机器上,示例输出为:
p0=7FFF2C75B89F, p1=7FFF2C75A89F, p2=7FFF2C75B89F
所以我们知道实现不会验证参数 -0x1000,否则无符号值将是一个非常大的整数。堆栈指针原本是 0x...B89F;由于此堆栈向上增长 alloca(0x1000),因此将堆栈指针更改为(0x...B89F - 0x1000) = 0x...A89F。在负分配 (0xA89F - (-0x1000)) 之后,堆栈指针回到 0x...B89F。
但是,对于 gcc 4.8.3,示例输出为:
p0=7FFFA3E27A90, p1=7FFFA3E26A80, p2=7FFFA3E27A70
在 /usr/include/alloca.h 我们发现:
#ifdef __GNUC__
# define alloca(size) __builtin_alloca (size)
#endif /* GCC. */
所以我们知道 gcc 4.8.3 提供的内置 alloca 函数做了类似的事情,只是它分配了额外的 0x10 字节作为安全边际。在进行负分配时,它仍然假设它向上增长,因此尝试保留 0x10 额外字节(- 0x10),因此 p2= 0x...6A80 - (-0x1000) - 0x10 = 0x...7A70。所以,要格外小心。
您不需要编写任何自定义freea(...)
类型的函数,也不需要使用 VLA。在 C 和 C++ 中可以轻松释放堆栈上分配的内存(C++ 不支持 VLA)。在alloca(...)
堆栈上分配,对吗?这意味着当内存超出范围时将被释放......所以只需使用范围!
#include <alloca.h>
int main()
{
{
void* ptr = alloca(1024);
// do your stuff
} // memory is deallocated here
return 0;
}
从 godbolt 在线编译器可以看出,程序集(没有优化)做了正确的事: https ://godbolt.org/z/Gn5YMa