命名空间
变体
操作

外部和暂定定义

来自 cppreference.cn
< c‎ | language

翻译单元的顶层(即经过预处理器处理后的源文件和所有 #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 声明不同,暂定定义可能与同一标识符的另一个声明在链接上不一致。如果同一标识符的两个声明在作用域内并且具有不同的链接,则行为未定义

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 外部定义 (p: TBD)
  • 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 外部定义