【发布时间】:2010-09-21 22:23:50
【问题描述】:
是否可以在当前函数退出之前显式释放 C 的 alloca() 分配的内存?如果有,怎么做?
【问题讨论】:
-
你能解释一下你的动机吗?为什么要在返回之前释放分配的空间?
标签: optimization memory-management
是否可以在当前函数退出之前显式释放 C 的 alloca() 分配的内存?如果有,怎么做?
【问题讨论】:
标签: optimization memory-management
来自http://www.gnu.org/software/libc/manual/html_mono/libc.html#Variable-Size-Automatic:
使用
alloca分配块是显式操作;您可以根据需要分配任意数量的块,并在运行时计算大小。但是,当您退出调用 alloca 的函数时,所有块都会被释放,就像它们是在该函数中声明的自动变量一样。 无法明确释放空间。
【讨论】:
这是可能的,但没有预先编写的函数可以做到这一点。您必须深入研究编译器的 alloca() 实现以弄清楚它在做什么,然后编写自己的 freea()。由于每个编译器执行 alloca() 的方式不同,因此您必须为每个编译器重写 freea()。
但我很难相信这值得麻烦。如果您需要显式释放它,只需使用 malloc/free - 这些功能通常都经过大量优化。充分利用它们。
【讨论】:
a=alloca(asize);b=alloca(bsize); 你能描述一下freea(a); 电话会做什么吗?
您正在使用 alloca() 在堆栈上进行分配;如果之后发生了其他事情(并且如果不将所有内容都写入汇编中就无法控制它),则不能只是将堆栈缩小。因此,除非您离开函数的堆栈框架,否则这是不可能的。
这也是为什么如果您溢出分配的缓冲区,您真的会搞砸事情。你可以开始覆盖你的函数返回的代码地址,导致它跳转到其他地方,各种可怕的东西。小心!
Malloc 在堆上工作,因此它的功能更加灵活。
【讨论】:
使用 C99,您可以使用 Variable Length Array 实现相同的目的。只需在新范围内声明 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() 的真正实现),让我们通过以下 non-可移植的代码例如:
#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) 因此将堆栈指针 up 更改为 (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
【讨论】: