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];
}
/* … */
}
Далее: Создание вызовов, Предыдущее: Метки как значения, Вверх: Расширения C [Оглавление][Индекс]
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-6.5.0/gcc/Nested-Functions.html