Внешние и предварительные определения
На верхнем уровне единицы трансляции (то есть исходного файла со всеми #includes после препроцессора) каждая C-программа представляет собой последовательность объявлений, которые объявляют функции и объекты с внешней или внутренней связью. Эти объявления известны как внешние объявления, потому что они появляются вне любой функции.
extern int n; // external declaration with external linkage
int b = 1; // external definition with external linkage
static const char *c = "abc"; // external definition with internal linkage
int f(void) { // external definition with external linkage
int a = 1; // non-external
return b;
}
static void x(void) { // external definition with internal linkage
}Объекты, объявленные с внешним объявлением, имеют статическую продолжительность хранения, и поэтому не могут использовать auto или register спецификаторы за исключением того, что auto может использоваться для вывода типа(с C23). Идентификаторы, введённые внешними объявлениями, имеют объём файла.
Предварительные определения
Предварительное определение — это внешнее объявление без инициализатора и либо без спецификатора класса хранения, либо со спецификатором static.
Предварительное определение — это объявление, которое может или не может действовать как определение. Если фактическое внешнее определение найдено ранее или позже в той же единице трансляции, то предварительное определение действует только как объявление.
int i1 = 1; // definition, external linkage int i1; // tentative definition, acts as declaration because i1 is defined extern int i1; // declaration, refers to the earlier definition extern int i2 = 3; // definition, external linkage int i2; // tentative definition, acts as declaration because i2 is defined extern int i2; // declaration, refers to the external linkage definition
Если определений в той же единице трансляции нет, то предварительное определение действует как фактическое определение, которое пусто инициализирует объект.
int i3; // tentative definition, external linkage int i3; // tentative definition, external linkage extern int i3; // declaration, external linkage // in this translation unit, i3 is defined as if by "int i3 = 0;"
В отличие от объявлений extern, которые не изменяют связь идентификатора, если предыдущее объявление его установило, предварительные определения могут расходиться по связи с другим объявлением того же идентификатора. Если два объявления для одного и того же идентификатора находятся в области видимости и имеют разные связи, поведение не определено:
static int i4 = 2; // definition, internal linkage int i4; // Undefined behavior: linkage disagreement with previous line extern int i4; // declaration, refers to the internal linkage definition static int i5; // tentative definition, internal linkage int i5; // Undefined behavior: linkage disagreement with previous line extern int i5; // refers to previous, whose linkage is internal
Предварительное определение с внутренней связью должно иметь полный тип.
static int i[]; // Error, incomplete type in a static tentative definition
int i[]; // OK, equivalent to int i[1] = {0}; unless redeclared later in this fileПравило одного определения
Каждая единица трансляции может иметь ноль или одно внешнее определение каждого идентификатора с внутренней связью (глобальная переменная с static).
Если идентификатор с внутренней связью используется в любом выражении, кроме не-VLA,(с C99) sizeof, _Alignof(с C11), или typeof(с C23), в единице трансляции должно быть одно и только одно внешнее определение для этого идентификатора.
Вся программа может иметь ноль или одно внешнее определение каждого идентификатора с внешней связью.
Если идентификатор с внешней связью используется в любом выражении, кроме не-VLA,(с C99) sizeof, _Alignof(с C11), или typeof(с C23), то где-то во всей программе должно быть одно и только одно внешнее определение для этого идентификатора.
Примечания
|
Встроенные определения в разных единицах трансляции не ограничены правилом одного определения. Подробности о встроенных определениях функций см. в |
(с C99) |
См. продолжительность хранения и связь для значения ключевого слова extern при объявлениях в области файла
См. определения для различий между объявлениями и определениями.
Предварительные определения были изобретены для стандартизации различных подходов, предшествующих C89, к предварительному объявлению идентификаторов с внутренней связью.
Ссылки
- Стандарт C17 (ISO/IEC 9899:2018):
- 6.9 Внешние определения (с. 113-116)
- Стандарт C11 (ISO/IEC 9899:2011):
- 6.9 Внешние определения (с. 155-159)
- Стандарт C99 (ISO/IEC 9899:1999):
- 6.9 Внешние определения (с. 140-144)
- Стандарт C89/C90 (ISO/IEC 9899:1990):
- 3.7 ВНЕШНИЕ ОПРЕДЕЛЕНИЯ
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/c/language/extern