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

时间:2019-03-15 07:10:22

标签: c function-pointers alloca

我正在尝试编写一个传递给函数以用作分配参数的函数;它应该接受类型为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);
}

是否有标准版本的行为方式不相同的原因?

3 个答案:

答案 0 :(得分:6)

谁说你的功能

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

有效吗?由__builtin_alloca分配的对象在函数结束时达到其生存期,因此,一旦返回它,就已经有一个悬空指针了!

答案 1 :(得分:5)

引用here中的手册页:

  

内联代码这一事实意味着无法接受   该函数的地址,或通过链接更改其行为   使用其他库。

页面还提到:

  如果有此功能的私有版本,则会造成

混乱的后果

答案 2 :(得分: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*选项)的glibc版本          <stdlib.h>包括<alloca.h>,其中包含以下行:

      #ifdef  __GNUC__
       #define alloca(size)   __builtin_alloca (size)
       #endif
     

如果拥有此功能的私有版本,将带来混乱的后果。

     

内联代码这一事实意味着无法接受         该函数的地址,或通过链接更改其行为          使用其他库。

     

内联代码通常由一条指令调整          堆栈指针,并且不检查堆栈溢出。因此,有          没有NULL错误返回。

     

BUGS

     

如果不能扩展堆栈帧,则没有错误指示。          (但是,分配失败后,该程序很可能会收到          SIGSEGV信号,如果它试图访问未分配的空间。)

     

在许多系统上,alloca()不能在参数列表中使用          函数调用的时间,因为alloca()保留的堆栈空间          将出现在函数中间的堆栈中          争论。