构造一个指向alloca的函数指针会导致链接器错误?

问题描述 投票:3回答:3

我试图写一个函数,传递一个函数用于分配作为其参数;它应该接受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);
}

有没有理由说标准版本的行为方式不一样?

c function-pointers alloca
3个回答
5
投票

引用here的手册页:

代码内联的事实意味着无法获取此函数的地址,或通过链接到不同的库来更改其行为。

该页面还提到:

如果有一个这个功能的私人版本,会产生混乱的后果


6
投票

谁说你的功能

static inline void *alloca(size_t n) {
    return __builtin_alloca(n);
}

作品? __builtin_alloca分配的对象在函数结束时符合它的生命周期,所以一旦你返回它,你就有了一个悬空指针!


2
投票

你不能做你的建议。 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()保留的堆栈空间将出现在函数参数的空间中间的堆栈中。

© www.soinside.com 2019 - 2024. All rights reserved.