Spec-Zone.ru › GCC 9

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-9.5.0/gcc/Nested-Functions.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API