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);
}

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

4

3 回答 3

7

谁说你的功能

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

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

于 2019-03-15T08:02:46.303 回答
5

从这里引用手册页:

代码被内联的事实意味着不可能获取这个函数的地址,或者通过链接不同的库来改变它的行为。

该页面还提到:

如果有人拥有此功能的私人版本,后果会很混乱

于 2019-03-15T07:15:27.013 回答
2

你不能按照你的建议去做。alloca是一种非常特殊的野兽,它只能在函数体内显式调用,而不能在函数调用的参数表达式中调用。

请注意,没有标准版本alloca. C 标准和 POSIX 都没有描述这个函数。

您公开的替代方法,alloca重新定义为内联函数调用__builtin_alloca不起作用:除其他问题外,返回的指针__builtin_alloca()仅在调用者返回之前有效,无论它是否内联。

linux 手册页非常明确:

[...]

描述

alloca()函数在调用者的堆栈帧中分配 size 个字节的空间。当被调用的函数alloca()返回给它的调用者时,这个临时空间会自动释放。

返回值

alloca()函数返回一个指向分配空间开头的指针。如果分配导致堆栈溢出,则程序行为未定义。

[...]

符合

此功能不在 POSIX.1 中。

有证据表明该alloca()功能出现在 32V、PWB、PWB.2、3BSD 和 4BSD 中。在 4.3BSD 中有一个手册页。Linux 使用 GNU 版本。

笔记

alloca()函数依赖于机器和编译器。对于某些应用程序,与使用malloc(3)plus相比,使用它可以提高效率free(3)。在某些情况下,它还可以简化使用longjmp(3)或 的应用程序中的内存释放siglongjmp(3)。否则,不鼓励使用它。

因为分配的空间是在堆栈帧内分配的,所以如果函数返回被调用或alloca()跳过,该空间会自动释放。longjmp(3)siglongjmp(3)

alloca()如果指向它的指针只是超出范围,则分配的空间不会自动释放。

不要尝试free(3)分配的空间alloca()

GNU 版本注意事项

alloca()通常,gcc(1)使用内联代码将调用转换为。当给出-ansi-std=c89-std=c99-std=c11选项且不包含标题时,不会执行此操作<alloca.h>。否则,(没有-ansior-std=c*选项)glibc 版本的 <stdlib.h>包含<alloca.h>和包含以下行:

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

如果有人拥有此功能的私有版本,则会产生混乱的后果。

代码被内联的事实意味着不可能获取这个函数的地址,或者通过链接不同的库来改变它的行为。

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

错误

如果堆栈帧无法扩展,则没有错误指示。(然而,在分配失败后,SIGSEGV如果程序试图访问未分配的空间,它很可能会收到一个信号。)

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

于 2019-03-15T08:41:04.373 回答