为什么 GCC 中嵌套函数(GNU 扩展)的地址被编译器认为是 "not constant"?

Why is the address of a nested function (GNU extension) in GCC considered "not constant" by the compiler?

GNU C 编译器包含一个很好的 C 语言扩展,称为 Nested Functions。 但是,文档在某些方面不清楚。例如,它说

It is possible to call the nested function from outside the scope of its name by storing its address or passing the address to another function [...]

If you try to call the nested function through its address after the containing function exits, all hell breaks loose.

If you try to call it after a containing scope level exits, and if it refers to some of the variables that are no longer in scope, you may be lucky, but it’s not wise to take the risk.

If, however, the nested function does not refer to anything that has gone out of scope, you should be safe.

所以一方面,它说如果你在包含函数退出后调用嵌套函数,“一切都会崩溃”,但前面几句话说在某些情况下可以这样做所以。

我认为“事情超出范围”是指自动变量,因此重构应该是安全的

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

投诉 initializer element is not constant(即 nested_function)。

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

在当前的 GCC 实现中,仅在优化期间省略了嵌套函数不必要的静态链指针。它不会反映在类型系统中(与不绑定任何内容的 C++ lambda 不同)。如果没有优化,编译器必须在堆栈上创建一个蹦床,所以在这种情况下函数地址实际上是非常量。