C 参考手册
- C 语言
- C 的历史
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 静态断言
- 字符常量
- 函数声明
- 函数定义
- 转义序列
- 翻译阶段
- 标识符
- 作用域
- 生存期
- 查找与命名空间
- ASCII 码表
- 类型
- 遵从性
- 算术类型
- restrict 类型限定符
- 类型
- 对象与对齐
- 主函数
- 未定义行为
- 内存模型
- if 语句
- switch 语句
- for 循环
- while 循环
- do-while 循环
- continue语句
- break 语句
- goto语句
- return 语句
- 值类别
- 求值顺序
- 整数常量
- 浮点常量
- 字符串字面量
- 复合字面量
- 常量表达式
- 隐式转换
- 成员访问运算符
- 逻辑运算符
- 比较运算符
- 算术运算符
- 赋值运算符
- 自增/自减运算符
- 其他运算符
- sizeof 运算符
- _Alignof 运算符
- 转型运算符
- C 运算符优先级
- 泛型选择
- 标量初始化
- 数组初始化
- 结构体与联合体初始化
- 指针声明
- 数组声明
- 枚举
- 存储类指定符
- const 类型限定符
- volatile 类型限定符
- 结构体声明
- 联合体声明
- 位域
- _Alignas
- typedef 声明
- 原子类型
- 外部及试探性定义
- inline 函数指定符
- _Noreturn 函数指定符
- 变长参数
- 内联汇编
- 可分析性
- 替用运算符及记号
- C 关键词
- 预处理器
- C 标准库头文件
- 类型支持
- 程序支持工具
- 变参数函数
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 算法
- 数值
- 文件输入/输出
- 本地化支持
- 原子操作库
- 线程支持库
- 实验性 C 标准库
- 有用的资源
- 符号索引
- 注释
外部及试探性定义
在翻译单元的顶层(及在预处理器后拥有所有 #include 的源文件),每个 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
指定符。这些由外部声明引入的标识符拥有文件作用域。
试探性定义
试探性定义是没有初始化器的声明,且要么没有存储类指定符或拥有指定符 static
。
试探性定义是可能或可能不表现为定义的声明。若在同一翻译单元的前方或后方能找到实际的外部定义,则试探性定义仅表现为声明。
int i1 = 1; // 定义,外部链接 int i1; // 试探性定义,表现为声明,因为 i1 已定义 extern int i1; // 声明,引用前面的定义 extern int i2 = 3; // 定义,外部链接 int i2; // 试探性定义,表现为声明,因为 i2 已定义 extern int i2; // 声明,引用到前面的外部链接定义
若在同一翻译单元中无定义,则试探性定义表现为拥有初始化器 = 0 (对于数组、结构、联合类型则是 = {0} )的实际定义。
int i3; // 试探性定义,外部链接 int i3; // 试探性定义,外部链接 extern int i3; // 声明,外部链接 // 在此翻译单元中, i3 被如同“ int i3 = 0; ”的方式定义
不同于 extern 声明,若前一声明已建立标识符链接;则 extern 声明不更改链接,而试探性定义可以与同一标识符另一声明的链接不一致。若同一标识符的二个声明均在作用域内且拥有不同链接,则行为未定义:
static int i4 = 2; // 定义,内部链接 int i4; // 未定义行为:链接与前一行不一致 extern int i4; // 声明,引用到内部链接定义 static int i5; // 试探性定义,内部链接 int i5; // 未定义行为:链接与前一行不一致 extern int i5; // 引用到前者,其链接为内部
拥有内部链接的试探性定义必须拥有完整类型。
static int i[]; // 错误:试探性 static 声明中的不完整类型 int i[]; // OK,等价于 int i[1] = {0}; 除非在此文件之后重声明
一个定义规则
每个翻译单元可以拥有每个具有内部链接( static
全局名称)的零或一个外部定义。
若一个具有内部链接的标识符被用于任何异于非 VLA 的 (C99 起) sizeof ,或 _Alignof (C11 起)的表达式,则在该翻译单元中必须有且只有一个该标识符的外部定义。
整个程序可以拥有每个具有外部链接的标识符(异于 inline 函数) (C99 起)的零或一个外部定义。
若一个具有外部链接的标识符被用于任何异于非 VLA 的 (C99 起) sizeof ,或 _Alignof (C11 起)的表达式,则在整个程序中必须有且只有一个该标识符的外部定义。
注意
inline 函数定义的细节见 inline 。
关键词 extern
与文件作用域中声明在一起的含义,见存储期及链接。
声明与定义间的区别见定义。
发明试探性定义是为了标准化各种 C89 前的前置声明具有内部链接标识符的手段。
引用
- C11 standard (ISO/IEC 9899:2011):
- 6.9 External definitions (p: 155-159)
- C99 standard (ISO/IEC 9899:1999):
- 6.9 External definitions (p: 140-144)
- C89/C90 standard (ISO/IEC 9899:1990):
- 3.7 EXTERNAL DEFINITIONS