简体   繁体   English

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

[英]Constructing a function pointer to alloca causes linker errors?

I am trying to write a function that is passed a function to use for allocation as its argument; 我试图写一个函数,传递一个函数用于分配作为其参数; it should accept any valid allocator of type void *(*)(size_t) . 它应该接受任何类型为void *(*)(size_t)有效分配器。 However I am experiencing strange behavior when attempting to use alloca as the allocator - constructing a function pointer to the alloca function compiles fine but results in linker errors: 但是,当我尝试使用alloca作为分配器时,我遇到了奇怪的行为 - 构建一个指向alloca函数的函数指针编译得很好,但会导致链接器错误:

#include <stdlib.h>
#include <alloca.h>

int main() {
  void *(*foo)(size_t) = alloca;
}

results in 结果是

/tmp/cc8F67yC.o: In function `main':
test15.c:(.text+0x8): undefined reference to `alloca'
collect2: error: ld returned 1 exit status

Does this have something to do with alloca being inlined? 这与内联的alloca有关吗? But wouldn't inlining only be done as an optimization when the function doesn't need to have an address. 但是,当函数不需要具有地址时,不会仅作为优化进行内联。 In fact, with GCC I can even write my own version that does work as expected in the above code: 事实上,使用GCC,我甚至可以编写我自己的版本,它在上面的代码中按预期工作:

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

Is there a reason why the standard version doesn't behave the same way? 有没有理由说标准版本的行为方式不一样?

Who says your function 谁说你的功能

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

works? 作品? The object allocated by __builtin_alloca meets its lifetime at the end of the function so as soon as you return it, you've got a dangling pointer already! __builtin_alloca分配的对象在函数结束时符合它的生命周期,所以一旦你返回它,你就已经有了一个悬空指针!

Quoting the man pages from 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. 代码内联的事实意味着无法获取此函数的地址,或通过链接到不同的库来更改其行为。

The page also mentions: 该页面还提到:

messy consequences if one has a private version of this function 如果有一个这个功能的私人版本,会产生混乱的后果

You cannot do what you propose. 你不能做你的建议。 alloca is a very special beast, it can only be called explicitly inside a function body and not within the argument expressions of a function call. alloca是一个非常特殊的野兽,它只能在函数体内显式调用,而不能在函数调用的参数表达式中调用。

Note that there is no standard version of alloca . 请注意,没有标准版本alloca Neither the C Standard nor POSIX describe this function. C标准和POSIX都没有描述这个功能。

The alternative you expose, with alloca redefined as an inline function calling __builtin_alloca does not work: among other problems, the pointer returned by __builtin_alloca() is only valid until the caller returns, whether it is inlined or not. 您公开的替代方案,将alloca重新定义为调用__builtin_alloca的内联函数不起作用:除其他问题外, __builtin_alloca()返回的指针仅在调用者返回时才有效,无论是否内联。

The linux man page is very explicit: linux手册页非常明确:

[...] [...]

DESCRIPTION 描述

The alloca() function allocates size bytes of space in the stack frame of the caller. alloca()函数在调用者的堆栈帧中分配空间的大小字节。 This temporary space is automatically freed when the function that called alloca() returns to its caller. 当调用alloca()的函数返回其调用者时,将自动释放此临时空间。

RETURN VALUE 返回值

The alloca() function returns a pointer to the beginning of the allocated space. alloca()函数返回指向已分配空间开头的指针。 If the allocation causes stack overflow, program behavior is undefined. 如果分配导致堆栈溢出,则程序行为未定义。

[...] [...]

CONFORMING TO 符合

This function is not in POSIX.1. 此功能不在POSIX.1中。

There is evidence that the alloca() function appeared in 32V, PWB, PWB.2, 3BSD, and 4BSD. 有证据表明alloca()函数出现在32V,PWB,PWB.2,3BSD和4BSD中。 There is a man page for it in 4.3BSD. 在4.3BSD中有一个手册页。 Linux uses the GNU version. Linux使用GNU版本。

NOTES 笔记

The alloca() function is machine- and compiler-dependent. alloca()函数依赖于机器和编译器。 For certain applications, its use can improve efficiency compared to the use of malloc(3) plus free(3) . 对于某些应用,与使用malloc(3)free(3)相比,它的使用可以提高效率。 In certain cases, it can also simplify memory deallocation in applications that use longjmp(3) or siglongjmp(3) . 在某些情况下,它还可以简化使用longjmp(3)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) . 因为alloca()分配的空间是在堆栈帧中分配的,所以如果通过调用longjmp(3)siglongjmp(3)跳过函数返回,则会自动释放该空间。

The space allocated by alloca() is not automatically deallocated if the pointer that refers to it simply goes out of scope. 如果引用它的指针超出范围,则alloca()分配的空间不会自动释放。

Do not attempt to free(3) space allocated by alloca() ! 不要试图free(3) alloca()分配的空间!

Notes on the GNU version 关于GNU版本的注释

Normally, gcc(1) translates calls to alloca() with inlined code. 通常,gcc(1 alloca()使用内联代码转换对alloca()调用。 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. 如果给出-ansi-std=c89-std=c99-std=c11选项并且未包含标头<alloca.h>则不会执行此操作。 Otherwise, (without an -ansi or -std=c* option) the glibc version of <stdlib.h> includes <alloca.h> and that contains the lines: 否则,(没有-ansi-std=c*选项)glibc版本的<stdlib.h>包含<alloca.h>并且包含以下行:

  #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. 因此,没有返回NULL错误。

BUGS 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.) (但是,在分配失败后,如果程序尝试访问未分配的空间,程序可能会收到SIGSEGV信号。)

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. 在许多系统中, alloca()不能在函数调用的参数列表中使用,因为alloca()保留的堆栈空间将出现在函数参数的空间中间的堆栈中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM