Alloca()在堆栈上分配内存,而不是像malloc()那样在堆上分配内存。因此,当我从例程返回时,内存被释放。这实际上解决了动态分配内存的问题。通过malloc()释放分配的内存是一个非常令人头痛的问题,如果以某种方式错过释放会导致各种内存问题。
尽管有上述特性,为什么不鼓励使用alloca() ?
Alloca()在堆栈上分配内存,而不是像malloc()那样在堆上分配内存。因此,当我从例程返回时,内存被释放。这实际上解决了动态分配内存的问题。通过malloc()释放分配的内存是一个非常令人头痛的问题,如果以某种方式错过释放会导致各种内存问题。
尽管有上述特性,为什么不鼓励使用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的内联函数有关。它在程序执行的随机点表现为堆栈溢出(因为它在堆栈上分配)。
在头文件中:
void DoSomething() {
wchar_t* pStr = alloca(100);
//......
}
在实现文件中:
void Process() {
for (i = 0; i < 1000000; i++) {
DoSomething();
}
}
因此,发生的事情是编译器内联DoSomething函数,所有的堆栈分配都发生在Process()函数内部,从而使堆栈膨胀。在我的辩护中(我不是发现这个问题的人;当我无法修复它时,我不得不去和一个高级开发人员哭),它不是直接的分配,它是ATL字符串转换宏之一。
所以教训是-不要在你认为可能内联的函数中使用alloca。
alloca() is very useful if you can't use a standard local variable because its size would need to be determined at runtime and you can absolutely guarantee that the pointer you get from alloca() will NEVER be used after this function returns. You can be fairly safe if you do not return the pointer, or anything that contains it. do not store the pointer in any structure allocated on the heap do not let any other thread use the pointer The real danger comes from the chance that someone else will violate these conditions sometime later. With that in mind it's great for passing buffers to functions that format text into them :)
老问题了,但是没有人提到它应该被可变长度数组取代。
char arr[size];
而不是
char *arr=alloca(size);
它存在于标准C99中,并作为编译器扩展存在于许多编译器中。
这里的大多数回答都忽略了一点:使用_alloca()可能比仅仅在堆栈中存储大对象更糟糕,这是有原因的。
自动存储和_alloca()之间的主要区别是,后者有一个额外的(严重的)问题:分配的块不受编译器控制,因此编译器无法优化或回收它。
比较:
while (condition) {
char buffer[0x100]; // Chill.
/* ... */
}
:
while (condition) {
char* buffer = _alloca(0x100); // Bad!
/* ... */
}
后者的问题应该是显而易见的。
可悲的是,真正强大的alloca()在几乎强大的tcc中缺失了。Gcc确实有alloca()。
它播下了毁灭自己的种子。用return作为析构函数。 像malloc()一样,它在失败时返回一个无效的指针,这将在有MMU的现代系统上分段故障(希望重新启动那些没有MMU的系统)。 与自动变量不同,您可以在运行时指定大小。
它可以很好地用于递归。您可以使用静态变量来实现与尾递归类似的功能,并使用其他几个变量向每次迭代传递信息。
如果你推得太深,你肯定会出现段错误(如果你有一个MMU)。
注意,malloc()没有提供更多,因为当系统内存不足时,它会返回NULL(如果分配了NULL,也会出现段错误)。也就是说,你所能做的就是保释或试图以任何方式转让它。
要使用malloc(),我使用全局变量并将其赋值为NULL。如果指针不是NULL,我在使用malloc()之前释放它。
如果想复制任何现有数据,也可以使用realloc()作为一般情况。在使用realloc()之前,您需要检查指针,以确定是否要在realloc()之后复制或连接。
3.2.5.2 alloca的优点