Почему адрес вложенной функции (расширение GNU) в GCC считается компилятором не постоянным?

Компилятор GNU C содержит хорошее расширение для языка 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;
}

с жалобой на то, что initializer element is not constant (т.е. nested_function).

Есть ли причина для этого ограничения? (Я не могу представить, чтобы адрес функции был непостоянным, даже если он вложен)


person Maciek Godek    schedule 05.03.2020    source источник
comment
Первый абзац говорит о том, что выходит за пределы области имени, а не о выходе из функции. То есть его можно передать в качестве параметра другой функции из содержащей функции, которая имеет другую область видимости и не видит имя вложенной функции.   -  person Eugene Sh.    schedule 05.03.2020


Ответы (1)


В текущей реализации GCC ненужный указатель статической цепочки для вложенных функций опускается только во время оптимизации. Это не отражено в системе типов (в отличие от лямбды C++, которая ничего не связывает). Без оптимизации компилятору приходится создавать трамплин в стеке, поэтому адрес функции в этом случае на самом деле непостоянен.

person Florian Weimer    schedule 05.03.2020