## extern ## 判别规则(核心,唯一要记的) - 跨文件共享 = 一个文件**定义**(不带 extern)+ 其他文件用 `extern` **声明**(不带初始化) - 判断声明/定义:**`extern` + 无初始化 = 声明;否则(无 extern 或带初始化)= 定义** - 陷阱:`extern int x = 5;` 带初始化 → 是定义,extern 被"升级"成定义 一句话:**"extern 只借不造——声明是借,定义是造;借了没人造就链接报错,造了两次也报错。"** ## 抽象描述(一句话本质) extern 是跨文件共享的"介绍信"——告诉编译器这个变量/函数在别的文件里已定义,这里只是引用,不分配存储。函数声明默认就是 extern。 ## 正反例(建立直觉) - **正例1**:`file1.c: int counter = 0;`(定义,分配内存)+ `file2.c: extern int counter;`(声明,不分配)→ file2.c 能正常读写 counter - **正例2**:跨文件共享函数 `file1.c: int add(int a,int b){...}` + `file2.c: extern int add(int,int);` → 可调用。函数声明默认 extern,不写也等价 - **反例1**:只声明没定义 → 编译能过,链接报 `undefined reference`(未定义引用) - **反例2**:两个文件都定义没加 static → 链接报 `multiple definition`(重复定义)。解决:一个定义 + 一个 extern 声明,或都加 static 私有化 ## 易混对比 | | 定义 | 声明 | | ---------- | ------------------------------------ | ------------------------------------ | | 写法 | `int x = 5;`(无 extern / 带初始化) | `extern int x;`(extern + 无初始化) | | 分配内存 | ✅ 分配 | ❌ 不分配 | | 能出现几次 | 整个工程只能 1 次 | 可以很多次 | | 常见报错 | 重复定义(多个定义) | 未定义引用(只有声明没定义) | ## 变体验证(3 题,全过=学会) 1. `file1.c: int num = 10;` + `file2.c: extern int num; num++;` → 链接成功,num = **11** 2. 陷阱题:头文件 `common.h` 写 `int global;`,a.c 和 b.c 都 include → **报"重复定义"错误**。因为 `#include` 是文本展开,两个文件各有一份定义(外部链接) 3. `file2.c: extern int counter;` 但 file1.c 定义的是 `static int counter;` → **链接失败**。static = 内部链接,只登记在自己 .o 内部符号表、不进全局符号表,extern 去全局表找不到 → undefined reference。**extern 突破不了 static** ## 口述要点(面试怎么讲) - **结论先行**:声明是借、定义是造;定义只能有一份,其他文件 extern 声明引用 - **头文件正误姿势**:头文件只写 `extern int x;`(接口),定义 `int x = 0;` 只放唯一一个 .c(实现);多个文件 `#include "x.h"` 全都能用 - **为什么不 include .c 文件**:include .c = 把实现原样复制进每个文件 → 每个文件都有一份实现 → 重复定义;且 .c 是实现、头文件是接口,include 实现文件会编译变慢、依赖混乱、破坏架构分层 - **易错点**:`extern int x = 5;` 带初始化是定义不是声明;函数声明默认 extern 可省 ## 关系网络 | 相邻概念 | 和 extern 的关系 | 孤立理解会犯的错 | | ------------ | --------------------------------------------------------------------------------------- | --------------------------------------------------- | | static | 双胞胎,正好相反:static = 内部链接(锁文件内),extern = 外部链接(跨文件共享) | 以为 extern 能突破 static——不能,链接器不看内部符号 | | #include | extern 是"借"(声明,不分配),include 是"展开"(文本粘贴) | 以为 include 头文件 = 定义了变量 | | 声明 vs 定义 | `extern` + 无初始化 = 声明;无 extern 或带初始化 = 定义(`extern int x = 5;` 是定义!) | 以为 extern 永远只是声明 | | 头文件 | 头文件放 extern 声明 = 接口;.c 放定义 = 实现 | 头文件放定义 → 多文件 include 重复定义报错 | ## 学习日期 2026-08-03 --- _每主题一页,复习时只翻本目录。_