Namespaces
Variants

Scope

From cppreference.net

Каждый идентификатор , который появляется в программе на C, видим (то есть может быть использован) только в некоторой возможно несвязной части исходного кода, называемой его областью видимости .

В пределах области видимости идентификатор может обозначать более одной сущности только в том случае, если эти сущности находятся в разных пространствах имён .

C имеет четыре вида областей видимости:

  • область видимости блока
  • область видимости файла
  • область видимости функции
  • область видимости прототипа функции

Содержание

Вложенные области видимости

Если два различных объекта, названные одним и тем же идентификатором, находятся в области видимости одновременно, и они принадлежат одному и тому же пространству имён , области видимости вложены (никакая другая форма перекрытия областей видимости не допускается), и объявление, появляющееся во внутренней области видимости, скрывает объявление, появляющееся во внешней области видимости:

// Пространство имен здесь представляет собой обычные идентификаторы.
int a;   // область видимости файла для имени a начинается здесь
void f(void)
{
    int a = 1; // область видимости блока для имени a начинается здесь; скрывает a с областью видимости файла
    {
      int a = 2;         // область видимости внутреннего a начинается здесь, внешний a скрыт
      printf("%d\n", a); // внутренний a находится в области видимости, выводит 2
    }                    // область видимости блока для внутреннего a заканчивается здесь
    printf("%d\n", a);   // внешний a находится в области видимости, выводит 1
}                        // область видимости внешнего a заканчивается здесь
void g(int a);   // имя a имеет область видимости прототипа функции; скрывает a с областью видимости файла

Область видимости блока

Область видимости любого идентификатора, объявленного внутри составного оператора , включая тела функций, или в любом выражении, объявлении или операторе, присутствующем в if , switch , for , while , или do-while операторе (since C99) , или в списке параметров определения функции , начинается в точке объявления и заканчивается в конце блока или оператора, в котором он был объявлен.

void f(int n)  // область видимости параметра функции 'n' начинается
{         // тело функции начинается
   ++n;   // 'n' находится в области видимости и ссылается на параметр функции
// int n = 2; // ошибка: невозможно переобъявить идентификатор в той же области видимости
   for(int n = 0; n<10; ++n) { // область видимости циклической локальной переменной 'n' начинается
       printf("%d\n", n); // выводит 0 1 2 3 4 5 6 7 8 9
   } // область видимости циклической локальной переменной 'n' заканчивается
     // параметр функции 'n' снова в области видимости
   printf("%d\n", n); // выводит значение параметра
} // область видимости параметра функции 'n' заканчивается
int a = n; // Ошибка: имя 'n' не в области видимости

До стандарта C99 операторы выбора и итерации не создавали собственных областей видимости блока (хотя если в операторе использовался составной оператор, он имел свою обычную область видимости блока):

enum {a, b};
int different(void)
{
    if (sizeof(enum {b, a}) != sizeof(int))
        return a; // a == 1
    return b; // b == 0 in C89, b == 1 in C99
}
(начиная с C99)

Блочные переменные имеют внутреннюю связь и автоматическую продолжительность хранения по умолчанию. Следует отметить, что продолжительность хранения для не-VLA локальных переменных начинается при входе в блок, но до момента объявления переменная не находится в области видимости и не может быть доступна.

Область видимости файла

Область видимости любого идентификатора, объявленного вне любого блока или списка параметров, начинается с точки объявления и заканчивается в конце единицы трансляции.

int i; // область видимости i начинается
static int g(int a) { return a; } // область видимости g начинается (примечание: "a" имеет блочную область видимости)
int main(void)
{
    i = g(2); // i и g находятся в области видимости
}

Идентификаторы в области видимости файла имеют по умолчанию внешнюю линковку и статическую продолжительность хранения .

Область видимости функции

Метка (и только метка) объявленная внутри функции находится в области видимости повсюду в этой функции, во всех вложенных блоках, до и после её собственного объявления. Примечание: метка объявляется неявно, путём использования иначе неиспользуемого идентификатора перед двоеточием перед любым оператором.

void f()
{
   {   
       goto label; // метка в области видимости, несмотря на объявление позже
label:;
   }
   goto label; // метка игнорирует область видимости блока
}
void g()
{
    goto label; // ошибка: метка не в области видимости в g()
}

Область видимости прототипа функции

Область видимости имени, введенного в списке параметров объявления функции , которое не является определением, заканчивается в конце декларатора функции.

int f(int n,
      int a[n]); // n находится в области видимости и ссылается на первый параметр

Обратите внимание, что если в объявлении присутствуют несколько или вложенные деклараторы, область видимости заканчивается в конце ближайшего охватывающего функционального декларатора:

void f ( // имя функции 'f' находится в области видимости файла
 long double f,            // идентификатор 'f' теперь в области видимости, файловая 'f' скрыта
 char (**a)[10 * sizeof f] // 'f' ссылается на первый параметр, который находится в области видимости
);
enum{ n = 3 };
int (*(*g)(int n))[n]; // область видимости параметра функции 'n'
                       // заканчивается в конце его функционального декларатора
                       // в деклараторе массива глобальная n находится в области видимости
// (это объявляет указатель на функцию, возвращающую указатель на массив из 3 int)

Точка объявления

Область видимости тегов структур, объединений и перечислений начинается сразу после появления тега в спецификаторе типа, который объявляет этот тег.

struct Node {
   struct Node* next; // Node находится в области видимости и ссылается на эту структуру
};

Область видимости константы перечисления начинается сразу после появления её определяющего перечислителя в списке перечислителей.

enum { x = 12 };
{
    enum { x = x + 1, // новый x не в области видимости до запятой, x инициализируется значением 13
           y = x + 1  // новый перечислитель x теперь в области видимости, y инициализируется значением 14
         };
}

Область видимости любого другого идентификатора начинается сразу после конца его декларатора и перед инициализатором, если он есть:

int x = 2; // область видимости первого 'x' начинается
{
    int x[x]; // область видимости вновь объявленного x начинается после декларатора (x[x]).
              // Внутри декларатора внешний 'x' все еще находится в области видимости.
              // Это объявляет VLA массив из 2 int.
}
unsigned char x = 32; // область видимости внешнего 'x' начинается
{
    unsigned char x = x;
            // область видимости внутреннего 'x' начинается до инициализатора (= x)
            // это не инициализирует внутренний 'x' значением 32,
            // это инициализирует внутренний 'x' его собственным, неопределённым значением
}
unsigned long factorial(unsigned long n)
// декларатор завершается, 'factorial' находится в области видимости с этой точки
{
   return n<2 ? 1 : n*factorial(n-1); // рекурсивный вызов
}

В качестве особого случая, область видимости имени типа , которое не является объявлением идентификатора, считается начинающейся сразу после места в имени типа, где идентификатор появился бы, если бы он не был опущен.

Примечания

До C89 идентификаторы с внешней связью имели область видимости файла, даже если они были объявлены внутри блока, и из-за этого компилятор C89 не обязан диагностировать использование extern-идентификатора, который вышел из области видимости (такое использование является неопределённым поведением).

Локальные переменные внутри тела цикла могут скрывать переменные, объявленные в инициализирующем выражении for цикла в C (их область видимости вложена), но не могут делать этого в C++.

В отличие от C++, в C нет области видимости структуры: имена, объявленные внутри объявления struct/union/enum, находятся в той же области видимости, что и объявление структуры (за исключением того, что элементы данных находятся в своём собственном пространстве имён членов ):

struct foo {
    struct baz {};
    enum color {RED, BLUE};
};
struct baz b; // baz находится в области видимости
enum color x = RED; // color и RED находятся в области видимости

Ссылки

  • Стандарт C23 (ISO/IEC 9899:2024):
  • 6.2.1 Области видимости идентификаторов, имен типов и составных литералов (стр.: TBD)
  • Стандарт C17 (ISO/IEC 9899:2018):
  • 6.2.1 Области видимости идентификаторов (стр.: 28-29)
  • Стандарт C11 (ISO/IEC 9899:2011):
  • 6.2.1 Области видимости идентификаторов (стр.: 35-36)
  • Стандарт C99 (ISO/IEC 9899:1999):
  • 6.2.1 Области видимости идентификаторов (стр.: 29-30)
  • Стандарт C89/C90 (ISO/IEC 9899:1990):
  • 3.1.2.1 Области видимости идентификаторов

Смотри также

Документация C++ для Область видимости