Внешние и предварительные определения
На верхнем уровне единицы трансляции (то есть исходного файла со всеми #include после препроцессора) любая программа на 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)(до C23), alignof(начиная с C23)или typeof(начиная с C23), в единице трансляции должно быть ровно одно внешнее определение этого идентификатора.
Вся программа может содержать ноль или одно внешнее определение каждого идентификатора с внешней связью.
Если идентификатор с внешней связью используется в каком-либо выражении, кроме не VLA,(начиная с C99) sizeof, _Alignof(начиная с C11)(до C23), alignof(начиная с C23)или typeof(начиная с C23), где-либо во всей программе должно быть ровно одно внешнее определение этого идентификатора.
Примечания
| На определения inline-функций в разных единицах трансляции правило одного определения не распространяется. Подробности об определениях inline-функций см. в статье | (начиная с 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 ВНЕШНИЕ ОПРЕДЕЛЕНИЯ
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/c/language/extern