Почему адрес вложенной функции (расширение GNU) в G CC считается компилятором "не постоянным"? - PullRequest
3 голосов
/ 05 марта 2020

Компилятор GNU C содержит красивое расширение для языка 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 по сравнению со своим собственным scope.

Однако я с удивлением обнаружил, что следующий код не компилируется

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).

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

1 Ответ

2 голосов
/ 05 марта 2020

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

...