Не надежно. Используйте отделение на новой странице.
Оказывается, на самом деле их не так-то просто реализовать должным образом.
Должна ли внутренняя функция иметь доступ к переменным содержащейся области? Если нет, то нет смысла его вкладывать; просто сделайте его статическим (чтобы ограничить видимость единицы перевода, в которой он находится) и добавьте комментарий, в котором говорится: «Это вспомогательная функция, используемая только myfunc ()».
Если вы хотите получить доступ к переменным содержащейся области видимости, вы в основном вынуждают его генерировать замыкания (альтернатива ограничивает то, что вы можете делать с вложенными функциями, настолько, чтобы сделать их бесполезными). Я думаю, что GCC на самом деле справляется с этим, создавая (во время выполнения) уникальный преобразователь для каждого вызова содержащей функцию, которая устанавливает указатель контекста, а затем вызывает вложенную функцию. В конечном итоге это оказывается довольно опасным взломом, и некоторые совершенно разумные реализации не могут этого сделать (например, в системе, которая запрещает выполнение записываемой памяти - что многие современные ОС делают по соображениям безопасности). Единственный разумный способ заставить его работать в целом - это заставить все указатели функций переносить аргумент скрытого контекста, а все функции принимать его (потому что в общем случае вы не знаете, когда вы его вызываете, является ли это закрытием или незакрытая функция). Это неуместно требовать в C как по техническим, так и по культурным причинам, поэтому мы застряли с возможностью либо использовать явные указатели контекста для имитации замыкания вместо функций вложенности, либо использовать язык более высокого уровня с необходимой инфраструктурой для сделай это правильно.
Я хотел бы процитировать что-нибудь из 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.
См. C FAQ 20.24 и руководство GCC для потенциальных проблем:
Если вы попытаетесь вызвать вложенную функцию через свой адрес после содержащая функция завершена, все ад вырвется наружу. Если вы попытаетесь назовите его после уровня содержания вышел, и если это относится к некоторым переменных, которых больше нет в прицел, может вам повезет, но это не так разумно рискнуть. Если, однако, вложенная функция не относится к все, что вышло за рамки, вы должны быть в безопасности.
На самом деле это не более серьезно, чем некоторые другие проблемные части стандарта C, поэтому я бы сказал, что причины в основном исторические (C99 на самом деле не , который отличается от K&R По функциям C).
В некоторых случаях могут быть полезны вложенные функции с лексической областью видимости (рассмотрим рекурсивную внутреннюю функцию, которой не требуется дополнительное пространство стека для переменных во внешней области видимости без необходимости в статической переменной ), но, надеюсь, вы можете быть уверены, что компилятор правильно встроит такие функции, т.е. решение с отдельной функцией будет более подробным.
Вложенные функции - очень тонкая вещь. Вы сделаете их закрытие? В противном случае они не имеют преимущества перед обычными функциями, поскольку не могут получить доступ к локальным переменным. Если да, то что вы делаете с переменными, распределенными в стеке? Вы должны поместить их в другое место, чтобы, если вы вызовете вложенную функцию позже, переменная все еще там. Это означает, что они занимают память, поэтому вам нужно выделить для них место в куче. Без GC это означает, что теперь за очистку функций отвечает программист. И т.д. ... C # делает это, но у них есть сборщик мусора, и это значительно более новый язык, чем C.
ANSI C была создана 20 лет назад. Возможно, между 1983 и 1989 годами комитет мог обсуждать это в свете состояния технологии компиляторов в то время, но если они это сделали, их рассуждения теряются в смутном и далеком прошлом.
Либо вы не разрешаете ссылки на локальные переменные содержащейся функции в содержащейся функции, и вложение - это просто функция определения области видимости без особого используйте, или вы делаете. Если вы это сделаете, это не такая простая функция: вы должны иметь возможность вызывать вложенную функцию из другой при доступе к правильным данным, и вы также должны учитывать рекурсивные вызовы. В этом нет ничего невозможного - методы хорошо известны этим и были хорошо освоены, когда был разработан C (в Algol 60 уже была эта функция). Но это усложняет организацию времени выполнения и компилятор и препятствует простому отображению на язык ассемблера (указатель функции должен нести информацию об этом; есть альтернативы, такие как использование gcc).
Также было бы несложно добавить функции-члены к структурам, но они также не входят в стандарт.
Функции не добавляются в стандарт C на основе того, легко ли их реализовать. Это комбинация многих других факторов, включая момент времени, когда был написан стандарт, и то, что было общим / практичным в то время.