Namespaces
Variants

External and tentative definitions

From cppreference.net

На верхнем уровне единицы трансляции (то есть исходного файла со всеми включениями после препроцессора), каждая программа на 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) , должно существовать одно и только одно внешнее определение для этого идентификатора где-либо во всей программе.

Примечания

Встроенные определения в разных единицах трансляции не ограничены правилом одного определения. См. inline для подробностей о встроенных определениях функций.

(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 ВНЕШНИЕ ОПРЕДЕЛЕНИЯ