2008-11-12 77 views
6

是否有可能在当前函数退出之前显式释放由C的alloca()分配的内存?如果是这样,怎么样?释放alloca分配的内存

+0

你能解释一下你的动机吗?为什么你想在返回之前释放分配的空间? – Motti 2008-12-07 20:22:08

回答

8

这是可能的,但没有预先写好的功能。你必须深入研究你的编译器的alloca()实现,找出它在做什么,然后编写你自己的freea()。由于每个编译器都以不同的方式执行alloca(),所以必须为每个编译器重写freea()。

但我觉得很难相信这是值得的麻烦。如果需要明确释放它,只需使用malloc/free,这些函数通常会进行大量优化。利用它们。

+3

一个可移植的实现是“void freea(void * p){} //只是伪造它”。 – paxdiablo 2008-11-12 06:49:06

1

不,因为它与本地变量一起被分配在堆栈上。如果你想要显式地释放内存,可以使用其中一种动态内存分配函数。

有没有混合允许你明确地自由让它在函数退出时自动释放,至少不在标准中。

2

您正在使用alloca()分配堆栈。如果事后发生了其他事情(如果没有在汇编中编写所有东西,你无法控制它),你不能只收缩堆栈。所以直到你离开函数的堆栈框架,这是不可能的。

这也是为什么你可以真正搞砸了,如果你溢出分配的缓冲区。你可以开始覆盖函数返回的代码地址,使它跳转到其他地方,各种可怕的东西。小心!

Malloc在堆上工作,所以这就是为什么它可以做的更灵活。

1

这对连续传球风格(CPS),而不是realloca很有用。

在将栈缩回到字符串的长度并调用下一个函数之前,可以调用一个函数来分配和操作栈顶的一个字符串。

没有概念上的原因,为什么不能有一个freea(),除了堆栈中最顶层的入口外,其他任何东西都是nop。

2

使用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 
    ... 
} 
1

是的,但是这取决于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; 
} 

在一个旧的x64机器铿锵2.9,样品输出为:

p0=7FFF2C75B89F, p1=7FFF2C75A89F, p2=7FFF2C75B89F 

所以我们知道实现验证参数-0x1 000,否则无符号值将是一个非常大的整数。堆栈指针最初是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. */ 

所以我们知道,内建ALLOCA由gcc 4.8.3提供的函数做了类似的事情,除了它分配额外的0x10字节作为安全边界。在进行负分配时,它仍然假设它向上增长,因此试图保留0x10个额外字节( - 0x10),所以p2 = 0x ... 6A80 - ( - 0x1000) - 0x10 = 0x ... 7A70。所以,要特别小心。