我试图写一个函数,传递一个函数用于分配作为其参数;它应该接受void *(*)(size_t)
类型的任何有效分配器。但是,当我尝试使用alloca
作为分配器时,我遇到了奇怪的行为 - 构造一个指向alloca
函数的函数指针编译得很好,但会导致链接器错误:
#include <stdlib.h>
#include <alloca.h>
int main() {
void *(*foo)(size_t) = alloca;
}
结果是
/tmp/cc8F67yC.o: In function `main':
test15.c:(.text+0x8): undefined reference to `alloca'
collect2: error: ld returned 1 exit status
这与内联的alloca有关吗?但是,当函数不需要具有地址时,不会仅作为优化进行内联。事实上,使用GCC,我甚至可以编写我自己的版本,它在上面的代码中按预期工作:
static inline void *alloca(size_t n) {
return __builtin_alloca(n);
}
有没有理由说标准版本的行为方式不一样?
谁说你的功能
static inline void *alloca(size_t n) {
return __builtin_alloca(n);
}
作品? __builtin_alloca
分配的对象在函数结束时符合它的生命周期,所以一旦你返回它,你就有了一个悬空指针!
你不能做你的建议。 alloca
是一个非常特殊的野兽,它只能在函数体内显式调用,而不能在函数调用的参数表达式中调用。
请注意,没有标准版本的alloca
。 C标准和POSIX都没有描述这个功能。
将alloca
重新定义为调用__builtin_alloca
的内联函数的公开替代方法不起作用:除其他问题外,__builtin_alloca()
返回的指针只有在调用者返回时才有效,无论是否内联。
linux man page非常明确:
[...]
描述
alloca()
函数在调用者的堆栈帧中分配空间的大小字节。当调用alloca()
的函数返回其调用者时,将自动释放此临时空间。返回值
alloca()
函数返回指向已分配空间开头的指针。如果分配导致堆栈溢出,则程序行为未定义。[...]
符合
此功能不在POSIX.1中。
有证据表明
alloca()
功能出现在32V,PWB,PWB.2,3BSD和4BSD中。在4.3BSD中有一个手册页。 Linux使用GNU版本。笔记
alloca()
函数依赖于机器和编译器。对于某些应用,与使用malloc(3)
和free(3)
相比,它的使用可以提高效率。在某些情况下,它还可以简化使用longjmp(3)
或siglongjmp(3)
的应用程序中的内存释放。否则,不鼓励使用它。因为
alloca()
分配的空间是在堆栈帧中分配的,所以如果通过调用longjmp(3)
或siglongjmp(3)
跳过函数返回,则会自动释放该空间。如果引用它的指针超出范围,则
alloca()
分配的空间不会自动解除分配。不要尝试
free(3)
分配的alloca()
空间!关于GNU版本的注释
通常,gcc(1)使用内联代码转换对
alloca()
的调用。当给出-ansi
,-std=c89
,-std=c99
或-std=c11
选项并且不包括标题<alloca.h>
时,不会这样做。否则,(没有-ansi
或-std=c*
选项)<stdlib.h>
的glibc版本包括<alloca.h>
并且包含以下行:#ifdef __GNUC__ #define alloca(size) __builtin_alloca (size) #endif
如果有这个功能的私人版本,会产生混乱的后果。
代码内联的事实意味着无法获取此函数的地址,或通过链接到不同的库来更改其行为。
内联代码通常由调整堆栈指针的单个指令组成,并且不检查堆栈溢出。因此,没有
NULL
错误返回。BUGS
如果无法扩展堆栈帧,则没有错误指示。 (但是,在分配失败后,如果程序尝试访问未分配的空间,程序可能会收到
SIGSEGV
信号。)在许多系统中,
alloca()
不能在函数调用的参数列表中使用,因为alloca()
保留的堆栈空间将出现在函数参数的空间中间的堆栈中。