Почему вложенные функции не поддерживаются стандартом C?

Не надежно. Используйте отделение на новой странице.

13
задан Alexis Wilke 6 July 2016 в 16:06
поделиться

7 ответов

Оказывается, на самом деле их не так-то просто реализовать должным образом.

Должна ли внутренняя функция иметь доступ к переменным содержащейся области? Если нет, то нет смысла его вкладывать; просто сделайте его статическим (чтобы ограничить видимость единицы перевода, в которой он находится) и добавьте комментарий, в котором говорится: «Это вспомогательная функция, используемая только myfunc ()».

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

12
ответ дан 1 December 2019 в 19:02
поделиться

Я хотел бы процитировать что-нибудь из BDFL (Гвидо ван Россум):

Это связано с тем, что определения вложенных функций не имеют доступа к локальные переменные окружающего блока - только глобальные переменные содержащий модуль. Это сделано для того, чтобы поиск глобалов не придется пройтись по цепочке словарей - как в C, всего два вложенные области: локальные и глобальные (и, кроме того, встроенные). Следовательно, вложенные функции имеют ограниченное применение. Это был deliberate decision, based upon experience with languages allowing arbitraries nesting such as Pascal and both Algols -- code with too many nested scopes is about as readable as code with too many GOTOs.

Emphasis is mine.

I believe he was referring to nested scope in Python (and as David points out in the comments, this was from 1993, and Python does support fully nested functions now) -- but I think the statement still applies.

The other part of it could have been closures.

If you have a function like this C-like code:

(*int()) foo() {
    int x = 5;
    int bar() {
        x = x + 1;
        return x;
    }
    return &bar;
}

If you use bar in a callback of some sort, what happens with x? This is well-defined in many newer, higher-level languages, but AFAIK there's no well-defined way to track that x in C -- does bar return 6 every time, or do successive calls to bar return incrementing values? That could have potentially added a whole new layer of complication to C's relatively simple definition.

10
ответ дан 1 December 2019 в 19:02
поделиться

См. C FAQ 20.24 и руководство GCC для потенциальных проблем:

Если вы попытаетесь вызвать вложенную функцию через свой адрес после содержащая функция завершена, все ад вырвется наружу. Если вы попытаетесь назовите его после уровня содержания вышел, и если это относится к некоторым переменных, которых больше нет в прицел, может вам повезет, но это не так разумно рискнуть. Если, однако, вложенная функция не относится к все, что вышло за рамки, вы должны быть в безопасности.

На самом деле это не более серьезно, чем некоторые другие проблемные части стандарта C, поэтому я бы сказал, что причины в основном исторические (C99 на самом деле не , который отличается от K&R По функциям C).

В некоторых случаях могут быть полезны вложенные функции с лексической областью видимости (рассмотрим рекурсивную внутреннюю функцию, которой не требуется дополнительное пространство стека для переменных во внешней области видимости без необходимости в статической переменной ), но, надеюсь, вы можете быть уверены, что компилятор правильно встроит такие функции, т.е. решение с отдельной функцией будет более подробным.

5
ответ дан 1 December 2019 в 19:02
поделиться

Вложенные функции - очень тонкая вещь. Вы сделаете их закрытие? В противном случае они не имеют преимущества перед обычными функциями, поскольку не могут получить доступ к локальным переменным. Если да, то что вы делаете с переменными, распределенными в стеке? Вы должны поместить их в другое место, чтобы, если вы вызовете вложенную функцию позже, переменная все еще там. Это означает, что они занимают память, поэтому вам нужно выделить для них место в куче. Без GC это означает, что теперь за очистку функций отвечает программист. И т.д. ... C # делает это, но у них есть сборщик мусора, и это значительно более новый язык, чем C.

4
ответ дан 1 December 2019 в 19:02
поделиться

ANSI C была создана 20 лет назад. Возможно, между 1983 и 1989 годами комитет мог обсуждать это в свете состояния технологии компиляторов в то время, но если они это сделали, их рассуждения теряются в смутном и далеком прошлом.

2
ответ дан 1 December 2019 в 19:02
поделиться

Либо вы не разрешаете ссылки на локальные переменные содержащейся функции в содержащейся функции, и вложение - это просто функция определения области видимости без особого используйте, или вы делаете. Если вы это сделаете, это не такая простая функция: вы должны иметь возможность вызывать вложенную функцию из другой при доступе к правильным данным, и вы также должны учитывать рекурсивные вызовы. В этом нет ничего невозможного - методы хорошо известны этим и были хорошо освоены, когда был разработан C (в Algol 60 уже была эта функция). Но это усложняет организацию времени выполнения и компилятор и препятствует простому отображению на язык ассемблера (указатель функции должен нести информацию об этом; есть альтернативы, такие как использование gcc).

0
ответ дан 1 December 2019 в 19:02
поделиться

Также было бы несложно добавить функции-члены к структурам, но они также не входят в стандарт.

Функции не добавляются в стандарт C на основе того, легко ли их реализовать. Это комбинация многих других факторов, включая момент времени, когда был написан стандарт, и то, что было общим / практичным в то время.

3
ответ дан 1 December 2019 в 19:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: