不行:这在堆栈通常实现的情况下是行不通的。堆栈上的变量占用一定范围的地址。下一个变量紧随其后,因此没有扩展的空间。考虑像这样的函数:
void f(int x) {
int i;
float *a = alloca(40 * sizeof(float));
int k;
…
}
函数序言后的堆栈大致如下:
----------------+-----+-----+-----+-----+-------------------+-----+---------------------
... | ret | x | i | a | a[] | k | ...
----------------+-----+-----+-----+-----+-------------------+-----+---------------------
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
previous frames f's frame free space at the top
没有空间让a
增长。
我展示了一个高度简化的例子:在现实世界中,变量最终会进入寄存器,即使它们最终会进入堆栈中,变量也可以被重新排序等等。但是只能将一个变量作为堆栈上的最后一个变量,并腾出空间来增长。
因此,如果存在realloca
,则只能应用于位于堆栈顶部的变量。(否则它将不得不移动所有其他位于其之上的内容,但这将需要更新对这些内容的所有现有指针,这通常是不可能的)。这将是一种非常有限的机制,因此支持此功能的好处非常小。支持它将具有显着的成本,因为编译器通常可以按照他们想要的顺序将东西放在堆栈上:这个特性将需要一种新机制来让编译器知道一个特定的变量必须放到顶部。
也许某个C实现有realloca
,但鉴于成本/效益比,这是不太可能的。
当然,如果
alloca
不使用堆栈分配策略,那么可以轻松实现
realloca
。但在堆栈上分配内存正是
alloca
的全部意义所在。如果你想要可调整大小的对象,你需要一个具有堆接口的内存管理结构,这就是
malloc
的作用。
实际上,在库中进行动态内存管理有几种可能的方法。
最常见的方法是在需要时调用malloc
、realloc
和free
。这就是它们的作用。
在某些环境中,支持自定义分配器很有用。您可以为库的用户提供选择传递指向malloc
、realloc
和free
替代实现的指针。当您想编写一个可移植的库,需要被自身完全可移植的代码使用时,这非常有用。不过,大多数情况下,想要使用自定义分配器的用户可以通过链接自己的malloc
和相关函数来实现。而且,即使这样做,也很少有用。
如果您需要在没有动态分配的环境中(例如安全关键环境)工作的代码,则也不应使用
alloca
。
alloca
比
malloc
更糟糕,因为它会导致不可预测的堆栈使用,并可能导致堆栈溢出,这将完全不被检测到,或者只能通过程序崩溃来检测。 如果您需要在函数中使用变量(或大量)临时内存,请让用户向您传递一个大小合适的缓冲区。
void f(size_t n, float *working_buffer);
如果您的代码大小允许,最好传递数组大小并进行验证。
int f(size_t n, float *working_buffer, size_t working_buffer_length)
{
if (working_buffer_length < 3 * n) return -EINVAL;
…
}
malloc
、calloc
或realloc
返回的,则行为未定义。 - Gerhardhalloca()
本身被认为是不安全的。请注意,可用空间取决于嵌套例程的数量,并且内存违规始终是致命的。此外,当您离开本地函数时,该内存将不可用。如果您只想在本地使用内存(每个DLL都将有一个本地堆),则堆分配策略不是问题,如果您想全局使用它,则该方法出错的原因如前所述。 - Frankie_C