Alloca()在堆栈上分配内存,而不是像malloc()那样在堆上分配内存。因此,当我从例程返回时,内存被释放。这实际上解决了动态分配内存的问题。通过malloc()释放分配的内存是一个非常令人头痛的问题,如果以某种方式错过释放会导致各种内存问题。

尽管有上述特性,为什么不鼓励使用alloca() ?


当前回答

可悲的是,真正强大的alloca()在几乎强大的tcc中缺失了。Gcc确实有alloca()。

它播下了毁灭自己的种子。用return作为析构函数。 像malloc()一样,它在失败时返回一个无效的指针,这将在有MMU的现代系统上分段故障(希望重新启动那些没有MMU的系统)。 与自动变量不同,您可以在运行时指定大小。

它可以很好地用于递归。您可以使用静态变量来实现与尾递归类似的功能,并使用其他几个变量向每次迭代传递信息。

如果你推得太深,你肯定会出现段错误(如果你有一个MMU)。

注意,malloc()没有提供更多,因为当系统内存不足时,它会返回NULL(如果分配了NULL,也会出现段错误)。也就是说,你所能做的就是保释或试图以任何方式转让它。

要使用malloc(),我使用全局变量并将其赋值为NULL。如果指针不是NULL,我在使用malloc()之前释放它。

如果想复制任何现有数据,也可以使用realloc()作为一般情况。在使用realloc()之前,您需要检查指针,以确定是否要在realloc()之后复制或连接。

3.2.5.2 alloca的优点

其他回答

每个人都已经指出了堆栈溢出潜在的未定义行为,但我应该提到的是,Windows环境有一个很好的机制来捕捉这种情况,使用结构化异常(SEH)和保护页面。由于堆栈只在需要时增长,因此这些保护页驻留在未分配的区域。如果你对它们进行分配(通过溢出堆栈),就会抛出一个异常。

您可以捕获这个SEH异常并调用_resetstkoflw来重置堆栈并继续您的快乐之路。这并不理想,但这是另一种机制,至少可以在事情发生时知道哪里出了问题。*nix可能有类似的东西,但我不知道。

我建议通过包装alloca并在内部跟踪它来限制您的最大分配大小。如果你真的很认真,你可以在函数的顶部设置一些作用域哨兵来跟踪函数作用域中的任何分配,并检查它与项目允许的最大数量是否一致。

此外,除了不允许内存泄漏之外,alloca也不会导致内存碎片,这是非常重要的。我不认为alloca是不好的做法,如果你明智地使用它,这基本上适用于所有事情。: -)

alloca的一个缺陷是longjmp将它倒带。

也就是说,如果你用setjmp保存一个上下文,然后分配一些内存,然后longjmp到上下文,你可能会失去分配的内存。堆栈指针回到原来的位置,因此内存不再保留;如果你调用一个函数或执行另一个分配,你将破坏原来的分配。

为了澄清,我在这里特别提到的是一种情况,即longjmp不返回发生分配的函数!相反,函数使用setjmp保存上下文;然后使用alloca分配内存,最后在该上下文中执行longjmp。该函数的分配内存没有全部释放;就是从setjmp开始分配的所有内存。当然,我说的是观察到的行为;据我所知,任何分配都没有这样的要求。

The focus in the documentation is usually on the concept that alloca memory is associated with a function activation, not with any block; that multiple invocations of alloca just grab more stack memory which is all released when the function terminates. Not so; the memory is actually associated with the procedure context. When the context is restored with longjmp, so is the prior alloca state. It's a consequence of the stack pointer register itself being used for allocation, and also (necessarily) saved and restored in the jmp_buf.

顺便说一句,如果这样工作的话,这提供了一种合理的机制来故意释放使用alloca分配的内存。

我曾经遇到过这种情况,这是一个bug的根本原因。

原因如下:

char x;
char *y=malloc(1);
char *z=alloca(&x-y);
*z = 1;

并不是说任何人都可以编写这段代码,但是您传递给alloca的size参数几乎肯定来自某种输入,它可能恶意地目的是让您的程序分配一个像这样巨大的值。毕竟,如果大小不是基于输入,或者不可能很大,为什么不声明一个小的、固定大小的本地缓冲区呢?

几乎所有使用alloca和/或C99 vlas的代码都有严重的错误,这些错误会导致崩溃(如果你幸运的话)或特权损害(如果你不那么幸运的话)。

实际上,alloca并不保证使用堆栈。 事实上,gcc-2.95的alloca实现使用malloc本身从堆中分配内存。此外,这个实现是有bug的,它可能会导致内存泄漏和一些意想不到的行为,如果你在一个块内调用它进一步使用goto。并不是说您永远都不应该使用它,但有时alloca会导致比它从me中释放更多的开销。

老问题了,但是没有人提到它应该被可变长度数组取代。

char arr[size];

而不是

char *arr=alloca(size);

它存在于标准C99中,并作为编译器扩展存在于许多编译器中。