6.4 Вложенные функции
Функция, вложенная функция, определяется внутри другой функции. Вложенные функции поддерживаются как расширение в GNU C, но не поддерживаются в GNU C++.
Имя вложенной функции локально для блока, где она определена. Например, здесь мы определяем вложенную функцию с именем square, и вызываем её дважды:
foo (double a, double b)
{
double square (double z) { return z * z; }
return square (a) + square (b);
}Вложенная функция может получить доступ ко всем переменным содержащей функции, видимым в момент её определения. Это называется лексическим охватом. Например, здесь мы показываем вложенную функцию, использующую унаследованную переменную с именем offset:
bar (int *array, int offset, int size)
{
int access (int *array, int index)
{ return array[index + offset]; }
int i;
/* … */
for (i = 0; i < size; i++)
/* … */ access (array, i) /* … */
}Определения вложенных функций разрешены внутри функций в тех местах, где разрешены определения переменных; то есть, в любом блоке, смешанные с другими объявлениями и операторами в этом блоке.
Возможен вызов вложенной функции извне области видимости её имени путём хранения её адреса или передачи адреса другой функции:
hack (int *array, int size)
{
void store (int index, int value)
{ array[index] = value; }
intermediate (store, size);
}
Здесь функция intermediate получает адрес store в качестве аргумента. Если intermediate вызывает store, аргументы, переданные store, используются для записи в array. Но эта техника работает только до тех пор, пока не завершится содержащая функция (hack в этом примере).
Если вы попытаетесь вызвать вложенную функцию по её адресу после завершения содержащей функции, произойдёт сбой. Если вы попытаетесь вызвать её после выхода из уровня содержащей области видимости, и если она ссылается на некоторые переменные, которые больше не находятся в области видимости, вам может повезти, но рисковать не стоит. Однако, если вложенная функция не ссылается ни на что, вышедшее из области видимости, вы должны быть в безопасности.
GCC реализует получение адреса вложенной функции с помощью техники, называемой трамплинами. Эта техника была описана в Lexical Closures for C++ (Thomas M. Breuel, USENIX C++ Conference Proceedings, 17-21 октября 1988 года).
Вложенная функция может перейти к метке, унаследованной от содержащей функции, при условии, что метка явно объявлена в содержащей функции (см. Местные объявленные метки). Такой переход мгновенно возвращается в содержащую функцию, выходя из вложенной функции, которая выполнила goto и любых промежуточных функций. Вот пример:
bar (int *array, int offset, int size)
{
__label__ failure;
int access (int *array, int index)
{
if (index > size)
goto failure;
return array[index + offset];
}
int i;
/* … */
for (i = 0; i < size; i++)
/* … */ access (array, i) /* … */
/* … */
return 0;
/* Control comes here from access
if it detects an error. */
failure:
return -1;
}Вложенная функция всегда имеет нулевую связь. Объявление с extern или static является ошибочным. Если вам нужно объявить вложенную функцию до её определения, используйте auto (что в противном случае не имеет смысла для объявлений функций).
bar (int *array, int offset, int size)
{
__label__ failure;
auto int access (int *, int);
/* … */
int access (int *array, int index)
{
if (index > size)
goto failure;
return array[index + offset];
}
/* … */
}
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-13.3.0/gcc/Nested-Functions.html