Scope
Каждый идентификатор , который появляется в программе на 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++
для
Область видимости
|