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

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


当前回答

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

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。

一个问题是,它不是标准的,尽管它得到了广泛的支持。在其他条件相同的情况下,我总是使用标准函数,而不是常用的编译器扩展。

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

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

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

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

仍然不鼓励使用分配,为什么?

我没有看到这样的共识。很多强大的专业人士;一些缺点:

C99 provides variable length arrays, which would often be used preferentially as the notation's more consistent with fixed-length arrays and intuitive overall many systems have less overall memory/address-space available for the stack than they do for the heap, which makes the program slightly more susceptible to memory exhaustion (through stack overflow): this may be seen as a good or a bad thing - one of the reasons the stack doesn't automatically grow the way heap does is to prevent out-of-control programs from having as much adverse impact on the entire machine when used in a more local scope (such as a while or for loop) or in several scopes, the memory accumulates per iteration/scope and is not released until the function exits: this contrasts with normal variables defined in the scope of a control structure (e.g. for {int i = 0; i < 2; ++i) { X } would accumulate alloca-ed memory requested at X, but memory for a fixed-sized array would be recycled per iteration). modern compilers typically do not inline functions that call alloca, but if you force them then the alloca will happen in the callers' context (i.e. the stack won't be released until the caller returns) a long time ago alloca transitioned from a non-portable feature/hack to a Standardised extension, but some negative perception may persist the lifetime is bound to the function scope, which may or may not suit the programmer better than malloc's explicit control having to use malloc encourages thinking about the deallocation - if that's managed through a wrapper function (e.g. WonderfulObject_DestructorFree(ptr)), then the function provides a point for implementation clean up operations (like closing file descriptors, freeing internal pointers or doing some logging) without explicit changes to client code: sometimes it's a nice model to adopt consistently in this pseudo-OO style of programming, it's natural to want something like WonderfulObject* p = WonderfulObject_AllocConstructor(); - that's possible when the "constructor" is a function returning malloc-ed memory (as the memory remains allocated after the function returns the value to be stored in p), but not if the "constructor" uses alloca a macro version of WonderfulObject_AllocConstructor could achieve this, but "macros are evil" in that they can conflict with each other and non-macro code and create unintended substitutions and consequent difficult-to-diagnose problems missing free operations can be detected by ValGrind, Purify etc. but missing "destructor" calls can't always be detected at all - one very tenuous benefit in terms of enforcement of intended usage; some alloca() implementations (such as GCC's) use an inlined macro for alloca(), so runtime substitution of a memory-usage diagnostic library isn't possible the way it is for malloc/realloc/free (e.g. electric fence) some implementations have subtle issues: for example, from the Linux manpage:

在许多系统中,alloca()不能在函数调用的参数列表中使用,因为由alloca()保留的堆栈空间将出现在堆栈中用于函数参数的空间中间。

我知道这个问题被标记为C,但作为一名c++程序员,我认为我应该使用c++来说明alloca的潜在效用:下面的代码(以及这里的ideone)创建了一个向量,跟踪不同大小的多态类型,这些类型是堆栈分配的(生命期与函数返回绑定),而不是堆分配的。

#include <alloca.h>
#include <iostream>
#include <vector>

struct Base
{
    virtual ~Base() { }
    virtual int to_int() const = 0;
};

struct Integer : Base
{
    Integer(int n) : n_(n) { }
    int to_int() const { return n_; }
    int n_;
};

struct Double : Base
{
    Double(double n) : n_(n) { }
    int to_int() const { return -n_; }
    double n_;
};

inline Base* factory(double d) __attribute__((always_inline));

inline Base* factory(double d)
{
    if ((double)(int)d != d)
        return new (alloca(sizeof(Double))) Double(d);
    else
        return new (alloca(sizeof(Integer))) Integer(d);
}

int main()
{
    std::vector<Base*> numbers;
    numbers.push_back(factory(29.3));
    numbers.push_back(factory(29));
    numbers.push_back(factory(7.1));
    numbers.push_back(factory(2));
    numbers.push_back(factory(231.0));
    for (std::vector<Base*>::const_iterator i = numbers.begin();
         i != numbers.end(); ++i)
    {
        std::cout << *i << ' ' << (*i)->to_int() << '\n';
        (*i)->~Base();   // optionally / else Undefined Behaviour iff the
                         // program depends on side effects of destructor
    }
}

我想没有人提到过这一点:在函数中使用alloca会阻碍或禁用一些本来可以应用在函数中的优化,因为编译器无法知道函数的堆栈帧的大小。

例如,C编译器常见的优化是在函数中消除帧指针的使用,而是相对于堆栈指针进行帧访问;所以还有一种通用寄存器。但如果在函数内部调用alloca,则sp和fp之间的差异对于函数的一部分是未知的,因此无法进行此优化。

考虑到alloca的使用很少,而且它作为标准函数的不光彩地位,编译器设计人员很可能会禁用任何可能导致alloca出现问题的优化,如果要使它与alloca一起工作需要付出更多的努力的话。

更新: 由于变长局部数组已经添加到C语言中,并且由于这些向编译器提出了与alloca非常相似的代码生成问题,我看到“使用的罕见性和阴暗状态”不适用于底层机制;但是我仍然怀疑使用alloca或VLA会损害使用它们的函数中的代码生成。我欢迎来自编译器设计人员的任何反馈。