External and tentative definitions
На верхнем уровне единицы трансляции (то есть исходного файла со всеми включениями после препроцессора), каждая программа на C представляет собой последовательность объявлений , которые объявляют функции и объекты с внешней или внутренней линковкой . Эти объявления известны как внешние объявления , поскольку они находятся вне любой функции.
extern int n; // внешнее объявление с внешней линковкой int b = 1; // внешнее определение с внешней линковкой static const char *c = "abc"; // внешнее определение с внутренней линковкой int f(void) // внешнее определение с внешней линковкой { int a = 1; // не внешнее return b; } static void x(void) // внешнее определение с внутренней линковкой { }
Объекты, объявленные с внешним объявлением, имеют статическую продолжительность хранения , и поэтому не могут использовать спецификаторы auto или register за исключением того, что auto может использоваться для вывода типа (начиная с C23) . Идентификаторы, введенные внешними объявлениями, имеют область видимости файла .
Содержание |
Предварительные определения
Тентетивное определение — это внешнее объявление без инициализатора и либо без спецификатора класса хранения , либо со спецификатором static .
Предварительное определение — это объявление, которое может выступать или не выступать в качестве определения. Если фактическое внешнее определение найдено ранее или позднее в той же единице трансляции, то предварительное определение действует только как объявление.
int i1 = 1; // определение, внешняя линковка int i1; // предположительное определение, действует как объявление, так как i1 уже определен extern int i1; // объявление, ссылается на ранее сделанное определение extern int i2 = 3; // определение, внешняя линковка int i2; // предположительное определение, действует как объявление, так как i2 уже определен extern int i2; // объявление, ссылается на определение с внешней линковкой
Если в той же единице трансляции нет определений, то предварительное определение действует как фактическое определение, которое пусто-инициализирует объект.
int i3; // пробное определение, внешняя компоновка int i3; // пробное определение, внешняя компоновка extern int i3; // объявление, внешняя компоновка // в данной единице трансляции i3 определяется как "int i3 = 0;"
В отличие от extern объявлений, которые не изменяют линковку идентификатора, если предыдущее объявление её установило, предположительные определения могут конфликтовать по линковке с другим объявлением того же идентификатора. Если два объявления для одного идентификатора находятся в области видимости и имеют разную линковку, поведение не определено:
static int i4 = 2; // определение, внутренняя линковка int i4; // Неопределённое поведение: конфликт линковки с предыдущей строкой extern int i4; // объявление, ссылается на определение с внутренней линковкой static int i5; // пробное определение, внутренняя линковка int i5; // Неопределённое поведение: конфликт линковки с предыдущей строкой extern int i5; // ссылается на предыдущее, чья линковка является внутренней
Предварительное определение с внутренней линковкой должно иметь полный тип.
static int i[]; // Ошибка, неполный тип в статическом предварительном определении int i[]; // OK, эквивалентно int i[1] = {0}; если не переопределено позднее в этом файле
Правило одного определения
Каждая единица трансляции может иметь ноль или одно внешнее определение для каждого идентификатора с внутренней линковкой (глобальная переменная с static ).
Если идентификатор с внутренним связыванием используется в любом выражении, кроме
не-VLA,
(since C99)
sizeof
,
_Alignof
(since C11)
(until C23)
,
alignof
(since C23)
, или
typeof
(since C23)
, должна существовать ровно одна внешняя декларация для этого идентификатора в единице трансляции.
Вся программа может иметь ноль или одно внешнее определение для каждого идентификатора с внешней линковкой .
Если идентификатор с внешней линковкой используется в любом выражении, кроме
не-VLA,
(since C99)
sizeof
,
_Alignof
(since C11)
(until C23)
,
alignof
(since C23)
, или
typeof
(since C23)
, должно существовать одно и только одно внешнее определение для этого идентификатора где-либо во всей программе.
Примечания
|
Встроенные определения в разных единицах трансляции не ограничены правилом одного определения. См.
|
(since C99) |
См. длительность хранения и связывание для понимания значения ключевого слова extern в объявлениях на уровне файла
См. определения для различения между объявлениями и определениями.
Предварительные определения были созданы для стандартизации различных подходов, существовавших до C89, к предварительному объявлению идентификаторов с внутренней линковкой.
Ссылки
- Стандарт C23 (ISO/IEC 9899:2024):
-
- 6.9 Внешние определения (стр.: TBD)
- Стандарт 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 ВНЕШНИЕ ОПРЕДЕЛЕНИЯ