命名空间
变体
操作

外部定义与暂定定义

来自 cppreference.cn
< 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
{
}

使用外部声明声明的对象具有静态存储期,因此不能使用 autoregister 说明符 ,除非 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 声明不同,如果先前的声明已建立标识符的链接,则 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

(自 C99 起)

有关文件作用域声明中关键字 extern 的含义,请参见 存储期和链接

有关声明和定义之间的区别,请参见 定义

发明暂定定义是为了标准化 C89 之前用于前向声明具有内部链接的标识符的各种方法。

[编辑] 参考

  • C23 标准 (ISO/IEC 9899:2024)
  • 6.9 外部定义 (页码:待定)
  • 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 外部定义