繁体   English   中英

为什么这个定义在堆栈中的 C 函数指针在传递时无效?

[英]Why is this C function pointer defined in the stack not valid when passed?

我试图将父块范围内定义的函数指针传递给另一个函数。 我在不同的环境中都遇到了工作和段错误。 (我不是C专家)

编码:

#include <stdio.h>
#include <stdbool.h>

void test_function(bool (*function_pointer) (int x)) {
        printf("addr passed function_pointer %p\n", function_pointer);
        if (function_pointer(100)) {
                printf("  run: true\n");
        } else {
                printf("  run: false\n");
        }
}

bool function_outside_main(int x) {
        return x < 0;
}

int main(void) {
        // run with function defined globally
        printf("addr function_outside_main %p\n", function_outside_main);
        test_function(function_outside_main);

        // run with function defined in this stack block
        bool function_inside_main(int x) {
                return x > 0;
        }
        printf("addr function_inside_main %p\n", function_inside_main);
        test_function(function_inside_main); // shouldn't the address be valid?
}

在带有 GCC 版本5.4.0 (在 Amazon EC2 上)的 Ubuntu 16.04.4 上,它与输出一起工作:

addr function_outside_main 0x400620
addr passed function_pointer 0x400620
  run: false
addr function_inside_main 0x7ffc018d5690
addr passed function_pointer 0x7ffc018d5690
  run: true

在带有 GCC 版本9.3.0 (在 Windows WSL 下)的 Ubuntu 20.04.1 上,它因段错误而失败:

addr function_outside_main 0x7ff19c8631dd
addr passed function_pointer 0x7ff19c8631dd
  run: false
addr function_inside_main 0x7ffffc033b20
addr passed function_pointer 0x7ffffc033b20
zsh: segmentation fault (core dumped)  ./a.out

像这样的嵌套函数是 gcc 扩展,而不是 C 标准的一部分。

gcc 为此通常使用的实现1涉及为嵌套函数创建一个堆栈上的 thunk,因此调用它需要可执行堆栈支持。 较新版本的 Linux(和 Windows)默认为不可执行的堆栈,因此会崩溃。

要使其工作,您可以使用 gcc 的-z execstack选项,或者您可以使用execstack工具修改二进制文件以在创建后指定可执行堆栈。


1在带有 -O 的某些版本的 gcc 中,它可以确定何时实际上不需要嵌套嵌套函数(它们从不引用包含范围),并且在这些情况下不使用 thunk

我无权访问 WSL(或 9.3 gcc),但我确实注意到一个警告,即内部函数在它定义的函数返回后可能不会被调用。 通常这对于延迟的函数来说会很麻烦,但我怀疑你的编译器已经折叠了最终语句( test_function(function_inside_main); // shouldn't the address be valid? )与函数结语; 所以你可以添加一行: printf("Hello, world\\n"); 在那句话之后,看看它是否神奇地修复了它?

这就是扩展又名定义您自己的类似 C 语言的危险。

使用这个 gcc 扩展,你不能在定义它的函数范围之外调用它。它是一个未定义的行为。 非常类似于取消引用函数作用域外的指针所引用的局部自动变量

暂无
暂无

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

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