由malloc
分配的内存可以用realloc
重新分配。 alloca
是否有类似的功能?当您不希望在堆上分配内存时,并且您需要多次分配可变的堆栈内存(例如,在需要动态内存但又不想动态存储的库函数中)时,重新分配堆栈内存可能会很有用。在堆上分配,因为库的用户可能使用自定义堆分配策略。看起来像这样:
int main(void) {
float * some_mem = alloca(40 * sizeof(float));
// do something with this memory...
// now we need a different amount of memory, but some_mem still occupies a lot of the stack, so just reallocate it.
// is something like this possible?
some_mem = realloca(some_mem, 50 * sizeof(float));
}
重要的是,这一切都发生在堆栈上。 问:有没有办法重新分配动态堆栈内存?
答案 0 :(得分:9)
否:这与通常实现的堆栈不兼容。堆栈上的变量占用固定的地址范围。下一个变量紧随其后,因此没有增长空间。考虑这样的函数:
void f(int x) {
int i;
float *a = alloca(40 * sizeof(float));
int k;
…
}
函数序号之后的堆栈如下所示:
----------------+-----+-----+-----+-------------------+-----+---------------------
... | ret | x | i | a | k | ...
----------------+-----+-----+-----+-------------------+-----+---------------------
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
previous frames f's frame free space at the top
a
没有增长空间。
我正在展示一个高度简化的示例:在现实世界中,变量最终存储在寄存器中,即使变量确实存在于堆栈中,也可以对其重新排序,等等。但是,只有一个变量可以是变量中的最后一个堆满了成长的空间。
因此,如果存在realloca
,则只能将其应用于堆栈顶部的变量。 (否则,它必须移动其上所有其他内容,但这将需要更新所有现有的指向这些对象的指针,通常这是不可能的。)这将是一个非常有限的机制,因此对此功能的支持必须很小的好处。支持它会产生巨大的成本,因为编译器通常可以自由地按所需顺序将它们放到堆栈中:此功能将需要一种新的机制,以使编译器知道一个特定的变量必须放在顶部。
某处的某些C实现可能具有realloca
,但考虑到成本/收益比,这不太可能。
如果realloca
不使用堆栈分配策略,当然可以轻松实现alloca
。但是在堆栈上进行分配是alloca
的重点。如果要调整对象的大小,则需要带有堆接口的内存管理结构,这就是malloc
的目的。
实际上,库中有几种可行的动态内存管理方法。
最常见的方法是在需要时调用malloc
,realloc
和free
。那就是他们的目的。
在某些环境中,支持自定义分配器很有用。您可以为库的用户提供选项,以将指针传递到malloc
,realloc
和free
的替代实现。当您要编写一个可移植的库,而该库需要由完全可移植的代码使用时,此功能很有用。但是,在大多数情况下,想要使用自定义分配器的用户可以通过链接自己的malloc
和朋友来做到这一点。甚至那也很少有用。
如果您需要可以在没有动态分配的环境中工作的代码(例如,对安全至关重要的环境),那么也不应使用alloca
。 alloca
比malloc
更糟糕,因为它会导致不可预知的堆栈使用情况,并可能导致根本无法检测到堆栈溢出,或者只能由程序崩溃检测到堆栈溢出。如果您在函数中需要可变(或大量)的临时内存,请让用户将适当大小的缓冲区传递给您。
/** [documentation of the function] …
* working_buffer must point to an array of floats of 3*n elements.
*/
void f(size_t n, float *working_buffer);
更好的是,如果您有代码大小预算,请传递数组大小并进行验证。
/** [documentation of the function] …
* working_buffer must point to an array of floats of 3*n elements.
*/
int f(size_t n, float *working_buffer, size_t working_buffer_length)
{
if (working_buffer_length < 3 * n) return -EINVAL;
…
}
答案 1 :(得分:3)
已被接受的答案正确地指出,realloca
通常没有足够的好处,因为分配很难“增长”。
我看到的另一个问题是,这些分配的生命周期一直到函数结束。当您将此指针传递给另一个函数并在那里调用realloca
时会发生什么?此函数将无法更改堆栈上较深的函数的堆栈框架。它也不能在自己的框架中重新分配它,因为该对象在返回时将被销毁,而原始对象仍然必须处于活动状态。
malloc/realloc
不存在此问题,因为堆具有全局生存期。
有人认为可以以这样一种方式定义语义,即只能在alloc
所在的函数中重新分配该函数。这大大减少了该函数的使用范围。