释放alloca分配的内存

时间:2008-11-12 04:18:57

标签: optimization memory-management

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

8 个答案:

答案 0 :(得分:9)

来自http://www.gnu.org/software/libc/manual/html_mono/libc.html#Variable-Size-Automatic

  

使用alloca分配块是一个明确的操作;您可以根据需要分配任意数量的块,并在运行时计算大小。但是当你退出调用alloca的函数时,所有的块都被释放,就好像它们是在该函数中声明的自动变量一样。 无法明确释放空间。

答案 1 :(得分:8)

有可能,但没有预先编写的功能。您必须深入研究编译器的alloca()实现,以弄清楚它在做什么,然后编写自己的freea()。由于每个编译器都以不同方式执行alloca(),因此您必须为每个编译器重写freea()。

但我发现很难相信这会值得这么麻烦。如果你需要明确地释放它,只需使用malloc / free - 这些功能通常都经过了大量优化。利用它们。

答案 2 :(得分:3)

您正在使用alloca()分配堆栈;如果之后发生了其他事情(如果不在程序集中编写所有内容就无法控制它),则不能只是缩小堆栈。所以,在你离开函数的堆栈框架之前,这是不可能的。

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

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

答案 3 :(得分: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
    ...
}

答案 4 :(得分:1)

不,因为它与局部变量一起分配在堆栈上。如果您想要可以显式释放的内存,请使用其中一个动态内存分配函数。

没有混合版本允许你明确地释放 AND 在功能退出时自动释放它,至少不在标准中。

答案 5 :(得分:1)

这对于连续传递样式(CPS)非常有用,而不是realloca。

你可以调用一个在堆栈顶部分配和操作字符串的函数,然后将堆栈缩减回字符串的长度并调用下一个函数。

没有概念上的理由说明为什么不能有一个freea(),除了堆栈中最顶层的条目之外,它将是一个nop。

答案 6 :(得分: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;
}

在具有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。所以,要格外小心。

答案 7 :(得分:0)

您无需编写任何自定义的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