Область видимости
Каждый идентификатор, встречающийся в программе на C, является видимым (то есть может быть использован) только в некоторой, возможно несвязной, части исходного кода, называемой его областью видимости.
В пределах одной области видимости идентификатор может обозначать более одной сущности только в том случае, если эти сущности находятся в разных пространствах имён.
В C существует четыре вида областей видимости:
- область видимости блока
- область видимости файла
- область видимости функции
- область видимости прототипа функции
Вложенные области видимости
Если две разные сущности, обозначенные одним и тем же идентификатором, одновременно находятся в области видимости и принадлежат одному и тому же пространству имён, области видимости являются вложенными (перекрытие областей видимости других видов не допускается), а объявление во внутренней области видимости скрывает объявление во внешней области видимости:
// The name space here is ordinary identifiers.
int a; // file scope of name a begins here
void f(void)
{
int a = 1; // the block scope of the name a begins here; hides file-scope a
{
int a = 2; // the scope of the inner a begins here, outer a is hidden
printf("%d\n", a); // inner a is in scope, prints 2
} // the block scope of the inner a ends here
printf("%d\n", a); // the outer a is in scope, prints 1
} // the scope of the outer a ends here
void g(int a); // name a has function prototype scope; hides file-scope a
Область видимости блока
Область видимости любого идентификатора, объявленного внутри составного оператора, включая тела функций, или в любом выражении, объявлении либо операторе, входящем в оператор if, switch, for, while или do-while(начиная с C99), либо в списке параметров определения функции, начинается в точке объявления и заканчивается в конце блока или оператора, в котором он был объявлен.
void f(int n) // scope of the function parameter 'n' begins
{ // the body of the function begins
++n; // 'n' is in scope and refers to the function parameter
// int n = 2; // error: cannot redeclare identifier in the same scope
for(int n = 0; n<10; ++n) { // scope of loop-local 'n' begins
printf("%d\n", n); // prints 0 1 2 3 4 5 6 7 8 9
} // scope of the loop-local 'n' ends
// the function parameter 'n' is back in scope
printf("%d\n", n); // prints the value of the parameter
} // scope of function parameter 'n' ends
int a = n; // Error: name 'n' is not in scope
| До 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; // scope of i begins
static int g(int a) { return a; } // scope of g begins (note, "a" has block scope)
int main(void)
{
i = g(2); // i and g are in scope
}
Идентификаторы с областью видимости файла по умолчанию имеют внешнее связывание и статическую длительность хранения.
Область видимости функции
Метка (и только метка), объявленная внутри функции, находится в области видимости во всей этой функции, во всех вложенных блоках, как до, так и после собственного объявления. Примечание: метка объявляется неявно — для этого перед двоеточием и любым оператором используется идентификатор, который иначе нигде не используется.
void f()
{
{
goto label; // label in scope even though declared later
label:;
}
goto label; // label ignores block scope
}
void g()
{
goto label; // error: label not in scope in g()
}
Область видимости прототипа функции
Область видимости имени, введённого в списке параметров объявления функции, не являющегося определением, заканчивается в конце декларатора функции.
int f(int n,
int a[n]); // n is in scope and refers to the first parameter
Обратите внимание: если в объявлении имеется несколько или вложенных деклараторов, область видимости заканчивается в конце ближайшего внешнего декларатора функции:
void f ( // function name 'f' is at file scope
long double f, // the identifier 'f' is now in scope, file-scope 'f' is hidden
char (**a)[10 * sizeof f] // 'f' refers to the first parameter, which is in scope
);
enum{ n = 3 };
int (*(*g)(int n))[n]; // the scope of the function parameter 'n'
// ends at the end of its function declarator
// in the array declarator, global n is in scope
// (this declares a pointer to function returning a pointer to an array of 3 int)
Точка объявления
Область видимости тегов структур, объединений и перечислений начинается сразу после появления тега в спецификаторе типа, объявляющем этот тег.
struct Node {
struct Node* next; // Node is in scope and refers to this struct
};
Область видимости константы перечисления начинается сразу после появления соответствующего элемента перечисления в списке перечислителей.
enum { x = 12 };
{
enum { x = x + 1, // new x is not in scope until the comma, x is initialized to 13
y = x + 1 // the new enumerator x is now in scope, y is initialized to 14
};
}
Область видимости любого другого идентификатора начинается сразу после конца его декларатора и до инициализатора, если он есть:
int x = 2; // scope of the first 'x' begins
{
int x[x]; // scope of the newly declared x begins after the declarator (x[x]).
// Within the declarator, the outer 'x' is still in scope.
// This declares a VLA array of 2 int.
}
unsigned char x = 32; // scope of the outer 'x' begins
{
unsigned char x = x;
// scope of the inner 'x' begins before the initializer (= x)
// this does not initialize the inner 'x' with the value 32,
// this initializes the inner 'x' with its own, indeterminate, value
}
unsigned long factorial(unsigned long n)
// declarator ends, 'factorial' is in scope from this point
{
return n<2 ? 1 : n*factorial(n-1); // recursive call
}
В особом случае область видимости имени типа, которое не является объявлением идентификатора, считается начинающейся сразу после места в имени типа, где находился бы идентификатор, если бы его не опустили.
Примечания
До C89 идентификаторы с внешним связыванием имели область видимости файла, даже если вводились внутри блока. Поэтому компилятор C89 не обязан диагностировать использование идентификатора с классом хранения extern, вышедшего из области видимости (такое использование является неопределённым поведением).
Локальные переменные внутри тела цикла могут скрывать переменные, объявленные в части инициализации цикла for в C (их области видимости вложены), но в C++ этого делать нельзя.
В отличие от C++, в C нет области видимости структуры: имена, объявленные внутри объявления структуры/объединения/перечисления, находятся в той же области видимости, что и объявление структуры (за исключением того, что поля данных находятся в собственном пространстве имён членов):
struct foo {
struct baz {};
enum color {RED, BLUE};
};
struct baz b; // baz is in scope
enum color x = RED; // color and RED are in scope
Ссылки
- Стандарт 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++ для области видимости |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/c/language/scope