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

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


当前回答

我遇到的最令人难忘的错误之一是与一个使用alloca的内联函数有关。它在程序执行的随机点表现为堆栈溢出(因为它在堆栈上分配)。

在头文件中:

void DoSomething() {
   wchar_t* pStr = alloca(100);
   //......
}

在实现文件中:

void Process() {
   for (i = 0; i < 1000000; i++) {
     DoSomething();
   }
}

因此,发生的事情是编译器内联DoSomething函数,所有的堆栈分配都发生在Process()函数内部,从而使堆栈膨胀。在我的辩护中(我不是发现这个问题的人;当我无法修复它时,我不得不去和一个高级开发人员哭),它不是直接的分配,它是ATL字符串转换宏之一。

所以教训是-不要在你认为可能内联的函数中使用alloca。

其他回答

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

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

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

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

原因如下:

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

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

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

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

char arr[size];

而不是

char *arr=alloca(size);

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

我遇到的最令人难忘的错误之一是与一个使用alloca的内联函数有关。它在程序执行的随机点表现为堆栈溢出(因为它在堆栈上分配)。

在头文件中:

void DoSomething() {
   wchar_t* pStr = alloca(100);
   //......
}

在实现文件中:

void Process() {
   for (i = 0; i < 1000000; i++) {
     DoSomething();
   }
}

因此,发生的事情是编译器内联DoSomething函数,所有的堆栈分配都发生在Process()函数内部,从而使堆栈膨胀。在我的辩护中(我不是发现这个问题的人;当我无法修复它时,我不得不去和一个高级开发人员哭),它不是直接的分配,它是ATL字符串转换宏之一。

所以教训是-不要在你认为可能内联的函数中使用alloca。

在我看来,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()可以在分配持久对象时极大地减少碎片。