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

Constructing a function pointer to alloca causes linker errors?

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

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

引用来自 here 的手册页:

The fact that the code is inlined means that it is impossible to take the address of this function, or to change its behavior by linking with a different library.

该页面还提到:

messy consequences if one has a private version of this function

谁说你的功能

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

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

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

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

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

linux man page 非常明确:

[...]

DESCRIPTION

The alloca() function allocates size bytes of space in the stack frame of the caller. This temporary space is automatically freed when the function that called alloca() returns to its caller.

RETURN VALUE

The alloca() function returns a pointer to the beginning of the allocated space. If the allocation causes stack overflow, program behavior is undefined.

[...]

CONFORMING TO

This function is not in POSIX.1.

There is evidence that the alloca() function appeared in 32V, PWB, PWB.2, 3BSD, and 4BSD. There is a man page for it in 4.3BSD. Linux uses the GNU version.

NOTES

The alloca() function is machine- and compiler-dependent. For certain applications, its use can improve efficiency compared to the use of malloc(3) plus free(3). In certain cases, it can also simplify memory deallocation in applications that use longjmp(3) or siglongjmp(3). Otherwise, its use is discouraged.

Because the space allocated by alloca() is allocated within the stack frame, that space is automatically freed if the function return is jumped over by a call to longjmp(3) or siglongjmp(3).

The space allocated by alloca() is not automatically deallocated if the pointer that refers to it simply goes out of scope.

Do not attempt to free(3) space allocated by alloca()!

Notes on the GNU version

Normally, gcc(1) translates calls to alloca() with inlined code. This is not done when either the -ansi, -std=c89, -std=c99, or the -std=c11 option is given and the header <alloca.h> is not included. Otherwise, (without an -ansi or -std=c* option) the glibc version of <stdlib.h> includes <alloca.h> and that contains the lines:

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

with messy consequences if one has a private version of this function.

The fact that the code is inlined means that it is impossible to take the address of this function, or to change its behavior by linking with a different library.

The inlined code often consists of a single instruction adjusting the stack pointer, and does not check for stack overflow. Thus, there is no NULL error return.

BUGS

There is no error indication if the stack frame cannot be extended. (However, after a failed allocation, the program is likely to receive a SIGSEGV signal if it attempts to access the unallocated space.)

On many systems alloca() cannot be used inside the list of arguments of a function call, because the stack space reserved by alloca() would appear on the stack in the middle of the space for the function arguments.