我试图弄清楚alloca()
在内存级别上实际上是如何工作的。关于linux man page:
alloca()函数的作用是在调用者的堆栈帧中分配大小字节的空间。当调用alloca()的函数返回给它的调用者时,这个临时空间会被自动释放。
这是否意味着alloca()
会将堆栈指针转发n
字节?或者新创建的内存分配到哪里?
这不是和variable length arrays完全一样吗?
我知道实现细节可能留给操作系统和其他东西。但我想知道一般情况下这是如何实现的。
我试图弄清楚alloca()
在内存级别上实际上是如何工作的。关于linux man page:
alloca()函数的作用是在调用者的堆栈帧中分配大小字节的空间。当调用alloca()的函数返回给它的调用者时,这个临时空间会被自动释放。
这是否意味着alloca()
会将堆栈指针转发n
字节?或者新创建的内存分配到哪里?
这不是和variable length arrays完全一样吗?
我知道实现细节可能留给操作系统和其他东西。但我想知道一般情况下这是如何实现的。
4条答案
按热度按时间t40tm48m1#
VLA不可能检测到分配失败;这是一个非常非C的东西强加在语言结构上。因此Alloca()的设计要好得多。
b0zn9rqh2#
是的,
alloca
在功能上等效于局部可变长度数组,即这一点:还有这个
两者都为堆栈上
int
类型的n
元素分配空间。arr
在每种情况下的唯一区别是1)一个是实际数组,另一个是指向数组第一个元素的指针,2)数组的生命周期结束于其封闭作用域,而alloca
内存的生命周期结束于函数返回时。在这两种情况下,数组都驻留在堆栈上。例如,给定以下代码:
当我运行它时,我得到:
这表明从
alloca
返回的内存位于两个VLA的内存之间。VLA最早出现在C99的C标准中,但
alloca
在此之前就已经存在了。Linux手册页指出:符合
此函数不在POSIX. 1 - 2001中。
有证据表明alloca()函数出现在32V、PWB、PWB.2、3BSD和4BSD中。在4.3BSD中有一个手册页。Linux使用GNU版本。
BSD 3可以追溯到70年代后期,因此
alloca
是在将其添加到标准之前对VLA的早期非标准化尝试。今天,除非你使用的编译器不支持VLA(如MSVC),否则没有理由使用这个函数,因为VLA现在是获得相同功能的标准化方法。
6vl6ewon3#
另一个answer精确地描述了VLA和
alloca()
的机制。然而,
alloca()
和 * 自动 * VLA之间存在显著的功能差异。对象的生存期。在
alloca()
的情况下,当函数返回时,生存期结束。对于VLA,对象在包含块结束时释放。因此,可以容易地耗尽回路中的堆,而不可能用VLA来完成。
vs
gojuced74#
尽管alloca从语法的Angular 看起来像一个函数,但它不能在现代编程环境中实现为一个普通的函数 *。它必须被视为具有类似函数接口的编译器特性。
传统的C编译器维护两个指针寄存器,一个“栈指针”和一个“帧指针”(或基址指针)。堆栈指针界定堆栈的当前范围。帧指针在进入函数时保存堆栈指针的值,并用于访问局部变量和在函数退出时恢复堆栈指针。
现在大多数编译器在正常函数中默认不使用帧指针。现代的调试/异常信息格式已经不需要它了,但是他们仍然理解它是什么,并且可以在需要的地方使用它。
特别是对于具有alloca或可变长度数组的函数,使用帧指针允许函数跟踪其堆栈帧的位置,同时动态修改堆栈指针以适应可变长度数组。
例如,我在O 1为arm构建了以下代码
我的评论(Comments Mine)
是的,alloca和可变长度数组非常相似(尽管另一个答案指出并不完全相同)。alloca似乎是两个构造中较老的。
这显然是它最初的实现方式(https://www.tuhs.org/cgi-bin/utree.pl?file=32V/usr/src/libc/sys/alloca.s)。
我想也有可能有一个实际的实现作为一个汇编函数,但有一个特殊的情况下,在编译器,使它进入愚蠢/可预测的模式时,它看到alloca,我不知道是否有任何编译器供应商这样做。