Alloca()在堆栈上分配内存,而不是像malloc()那样在堆上分配内存。因此,当我从例程返回时,内存被释放。这实际上解决了动态分配内存的问题。通过malloc()释放分配的内存是一个非常令人头痛的问题,如果以某种方式错过释放会导致各种内存问题。
尽管有上述特性,为什么不鼓励使用alloca() ?
Alloca()在堆栈上分配内存,而不是像malloc()那样在堆上分配内存。因此,当我从例程返回时,内存被释放。这实际上解决了动态分配内存的问题。通过malloc()释放分配的内存是一个非常令人头痛的问题,如果以某种方式错过释放会导致各种内存问题。
尽管有上述特性,为什么不鼓励使用alloca() ?
当前回答
正如在这篇新闻组帖子中提到的,有几个原因可以解释为什么使用alloca是困难和危险的:
并非所有编译器都支持alloca。 一些编译器对alloca的预期行为有不同的解释,因此即使在支持它的编译器之间也不能保证可移植性。 一些实现存在bug。
其他回答
alloca并不比变长数组(VLA)更糟糕,但它比在堆上分配更危险。
在x86上(最常见的是在ARM上),堆栈向下增长,这带来了一定的风险:如果你不小心写超出了用alloca分配的块(例如由于缓冲区溢出),那么你将覆盖你的函数的返回地址,因为它位于堆栈的“上面”,即在你分配的块之后。
这样做的后果是双重的:
程序将崩溃的壮观,它将不可能告诉为什么或哪里崩溃(堆栈将最有可能unwind到一个随机地址,由于覆盖的帧指针)。 它使缓冲区溢出的危险增加了许多倍,因为恶意用户可以制作一个特殊的有效负载,将其放在堆栈上,因此最终可以执行。
相反,如果你在堆上写超过一个块,你“只是”得到堆损坏。程序可能会意外终止,但会正确地展开堆栈,从而减少恶意代码执行的机会。
A place where alloca() is especially dangerous than malloc() is the kernel - kernel of a typical operating system has a fixed sized stack space hard-coded into one of its header; it is not as flexible as the stack of an application. Making a call to alloca() with an unwarranted size may cause the kernel to crash. Certain compilers warn usage of alloca() (and even VLAs for that matter) under certain options that ought to be turned on while compiling a kernel code - here, it is better to allocate memory in the heap that is not fixed by a hard-coded limit.
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的根本原因。
一个问题是,它不是标准的,尽管它得到了广泛的支持。在其他条件相同的情况下,我总是使用标准函数,而不是常用的编译器扩展。
在我看来,alloca()在可用的情况下,应该仅以受约束的方式使用。就像“goto”的使用一样,相当多理智的人不仅对alloca()的使用非常反感,而且对它的存在也非常反感。
对于嵌入式使用,其中堆栈大小是已知的,并且可以通过对分配大小的约定和分析施加限制,并且编译器不能升级到支持C99+,使用alloca()是很好的,而且我已经知道使用它。
When available, VLAs may have some advantages over alloca(): The compiler can generate stack limit checks that will catch out-of-bounds access when array style access is used (I don't know if any compilers do this, but it can be done), and analysis of the code can determine whether the array access expressions are properly bounded. Note that, in some programming environments, such as automotive, medical equipment, and avionics, this analysis has to be done even for fixed size arrays, both automatic (on the stack) and static allocation (global or local).
在堆栈上存储数据和返回地址/帧指针的架构上(据我所知,这就是它们的全部),任何堆栈分配变量都可能是危险的,因为变量的地址可以被取走,未检查的输入值可能会允许各种各样的恶作剧。
在嵌入式领域,可移植性不是一个太大的问题,但是它是反对在严格控制的环境之外使用alloca()的一个很好的理由。
在嵌入式空间之外,我主要在日志记录和格式化函数中使用alloca()以提高效率,并在非递归词法扫描器中使用,其中临时结构(使用alloca()在标记化和分类期间创建,然后在函数返回之前填充持久对象(通过malloc()分配)。对较小的临时结构使用alloca()可以在分配持久对象时极大地减少碎片。