6.12.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 реализует получение адреса вложенной функции с помощью приёма, называемого трамплинами. Этот приём описан в работе «Лексические замыкания для C++» (Thomas M. Breuel, материалы конференции USENIX по C++, 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-15.3.0/gcc/Nested-Functions.html