Alloca()在堆栈上分配内存,而不是像malloc()那样在堆上分配内存。因此,当我从例程返回时,内存被释放。这实际上解决了动态分配内存的问题。通过malloc()释放分配的内存是一个非常令人头痛的问题,如果以某种方式错过释放会导致各种内存问题。
尽管有上述特性,为什么不鼓励使用alloca() ?
Alloca()在堆栈上分配内存,而不是像malloc()那样在堆上分配内存。因此,当我从例程返回时,内存被释放。这实际上解决了动态分配内存的问题。通过malloc()释放分配的内存是一个非常令人头痛的问题,如果以某种方式错过释放会导致各种内存问题。
尽管有上述特性,为什么不鼓励使用alloca() ?
当前回答
这里的大多数回答都忽略了一点:使用_alloca()可能比仅仅在堆栈中存储大对象更糟糕,这是有原因的。
自动存储和_alloca()之间的主要区别是,后者有一个额外的(严重的)问题:分配的块不受编译器控制,因此编译器无法优化或回收它。
比较:
while (condition) {
char buffer[0x100]; // Chill.
/* ... */
}
:
while (condition) {
char* buffer = _alloca(0x100); // Bad!
/* ... */
}
后者的问题应该是显而易见的。
其他回答
alloca并不比变长数组(VLA)更糟糕,但它比在堆上分配更危险。
在x86上(最常见的是在ARM上),堆栈向下增长,这带来了一定的风险:如果你不小心写超出了用alloca分配的块(例如由于缓冲区溢出),那么你将覆盖你的函数的返回地址,因为它位于堆栈的“上面”,即在你分配的块之后。
这样做的后果是双重的:
程序将崩溃的壮观,它将不可能告诉为什么或哪里崩溃(堆栈将最有可能unwind到一个随机地址,由于覆盖的帧指针)。 它使缓冲区溢出的危险增加了许多倍,因为恶意用户可以制作一个特殊的有效负载,将其放在堆栈上,因此最终可以执行。
相反,如果你在堆上写超过一个块,你“只是”得到堆损坏。程序可能会意外终止,但会正确地展开堆栈,从而减少恶意代码执行的机会。
一个问题是,它不是标准的,尽管它得到了广泛的支持。在其他条件相同的情况下,我总是使用标准函数,而不是常用的编译器扩展。
我想没有人提到过这一点:在函数中使用alloca会阻碍或禁用一些本来可以应用在函数中的优化,因为编译器无法知道函数的堆栈帧的大小。
例如,C编译器常见的优化是在函数中消除帧指针的使用,而是相对于堆栈指针进行帧访问;所以还有一种通用寄存器。但如果在函数内部调用alloca,则sp和fp之间的差异对于函数的一部分是未知的,因此无法进行此优化。
考虑到alloca的使用很少,而且它作为标准函数的不光彩地位,编译器设计人员很可能会禁用任何可能导致alloca出现问题的优化,如果要使它与alloca一起工作需要付出更多的努力的话。
更新: 由于变长局部数组已经添加到C语言中,并且由于这些向编译器提出了与alloca非常相似的代码生成问题,我看到“使用的罕见性和阴暗状态”不适用于底层机制;但是我仍然怀疑使用alloca或VLA会损害使用它们的函数中的代码生成。我欢迎来自编译器设计人员的任何反馈。
原因如下:
char x;
char *y=malloc(1);
char *z=alloca(&x-y);
*z = 1;
并不是说任何人都可以编写这段代码,但是您传递给alloca的size参数几乎肯定来自某种输入,它可能恶意地目的是让您的程序分配一个像这样巨大的值。毕竟,如果大小不是基于输入,或者不可能很大,为什么不声明一个小的、固定大小的本地缓冲区呢?
几乎所有使用alloca和/或C99 vlas的代码都有严重的错误,这些错误会导致崩溃(如果你幸运的话)或特权损害(如果你不那么幸运的话)。
可悲的是,真正强大的alloca()在几乎强大的tcc中缺失了。Gcc确实有alloca()。
它播下了毁灭自己的种子。用return作为析构函数。 像malloc()一样,它在失败时返回一个无效的指针,这将在有MMU的现代系统上分段故障(希望重新启动那些没有MMU的系统)。 与自动变量不同,您可以在运行时指定大小。
它可以很好地用于递归。您可以使用静态变量来实现与尾递归类似的功能,并使用其他几个变量向每次迭代传递信息。
如果你推得太深,你肯定会出现段错误(如果你有一个MMU)。
注意,malloc()没有提供更多,因为当系统内存不足时,它会返回NULL(如果分配了NULL,也会出现段错误)。也就是说,你所能做的就是保释或试图以任何方式转让它。
要使用malloc(),我使用全局变量并将其赋值为NULL。如果指针不是NULL,我在使用malloc()之前释放它。
如果想复制任何现有数据,也可以使用realloc()作为一般情况。在使用realloc()之前,您需要检查指针,以确定是否要在realloc()之后复制或连接。
3.2.5.2 alloca的优点