为什么GCC编译器认为一个嵌套函数(GNU扩展)的地址是“非常量”?

4
GNU C编译器包含一个称为Nested Functions的C语言扩展,但其中一些点在文档中不够清晰。例如,它说:

通过存储其地址或将地址传递给另一个函数,可以从其名称范围之外调用嵌套函数。

如果在包含函数退出后通过其地址调用嵌套函数,则会出现问题。

如果在包含作用域级别退出后尝试调用它,并且它引用了不再在作用域内的某些变量,则您可能很幸运,但冒险并不明智。

然而,如果嵌套函数不引用任何已超出范围的内容,则应该是安全的。

因此,一方面,它说如果在包含函数退出后调用嵌套函数,“将会出现问题”,但是在几句话之后,它说有情况可以这样做。

我想他们所谓的“超出范围的内容”是自动变量,因此特别是重构应该是安全的。

static int f() { ... }

int outer() {
    ... // some use of f
}

进入

int outer() {
    int f() { ... } // f does not refer to outer's local variables
    ... // some use of f
}

如果模块中没有其他对f的使用,除了在outer函数中,即使假设outer函数以某种方式泄漏f地址超出其自身范围。

然而,我惊讶地发现以下代码无法编译

int main(void) {
    void nested_function(void) {}

    static const struct { void (*function_pointer)(void); } s = {
        .function_pointer = nested_function
    };

    return 0;
}

这个投诉是说初始化元素不是常量(即nested_function)。

这个限制有什么原因吗?(我无法想象一个函数的地址是非常量的,即使它是嵌套的)


1
第一段讨论的是“超出名称范围”,而不是函数退出后的情况。也就是说,它可以作为参数传递给另一个函数从包含函数内部,该函数具有不同的作用域,并且看不到嵌套函数的名称。 - Eugene Sh.
1个回答

2

在当前的GCC实现中,嵌套函数的不必要的静态链指针仅在优化期间被省略。它不反映在类型系统中(不像C++ lambda没有绑定任何东西)。如果没有进行优化,则编译器必须在栈上创建一个跳板,因此在这种情况下函数地址实际上是非常量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接