Spec-Zone.ru › C

Внешние и предварительные определения

На верхнем уровне единицы трансляции (то есть исходного файла со всеми #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-функций см. в статье 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API