命名空间
变体
操作

外部和暂定定义

来自 cppreference.com
< c‎ | 语言

翻译单元 (即带有所有 #includes 的源文件,在预处理器之后)的顶层,每个 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 起),或 typeof(自 C23 起) 之外的任何表达式中,则该翻译单元中必须有一个且仅有一个外部定义。

整个程序可以为具有 外部链接 的每个标识符提供零个或一个外部定义。

如果具有外部链接的标识符用在除 非 VLA,(自 C99 起) sizeof_Alignof(自 C11 起),或 typeof(自 C23 起) 之外的任何表达式中,则整个程序中必须有一个且仅有一个外部定义。

[编辑] 注释

不同翻译单元中的内联定义不受单定义规则约束。有关内联函数定义的详细信息,请参阅 inline

(自 C99 起)

有关在文件范围内的声明中使用关键字 extern 的含义,请参阅 存储期和链接

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

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

[编辑] 参考资料

  • C17 标准 (ISO/IEC 9899:2018)
  • 6.9 外部定义 (p: 113-116)
  • C11 标准 (ISO/IEC 9899:2011)
  • 6.9 外部定义 (p: 155-159)
  • C99 标准 (ISO/IEC 9899:1999)
  • 6.9 外部定义 (p: 140-144)
  • C89/C90 标准 (ISO/IEC 9899:1990)
  • 3.7 外部定义