extern
extern 是 C 语言中用来处理**“多文件协作”**的关键工具。
如果说 static 是把变量“藏起来”(私有化),那么 extern 就是把变量**“广而告之”**(公开化)。
它的核心含义只有一句话:“这个变量(或函数)是存在的,但不是在这里定义的,去别的文件找找吧。”
我们把定义 (Definition) 和 声明 (Declaration) 分开,就能彻底搞懂它。
1. 核心痛点:我想用隔壁文件的变量
假设你的 STM32 工程有两个文件:main.c 和 sensor.c。
- 你在
sensor.c里定义了一个变量int Temperature = 25;来存储温度。 - 现在,你想在
main.c里读取这个温度来决定是否报警。
如果你直接在 main.c 里写 if (Temperature > 30),编译器会报错:
"Error: 'Temperature' undeclared" (我不认识这个叫 Temperature 的家伙)。
这时候,就需要 extern 出马了。
2. extern 怎么用?
你需要在 main.c 里告诉编译器:“老兄,别报错,Temperature 这个变量是有的,只不过它住在隔壁 sensor.c 里,待会儿链接的时候你能找到它的。”
代码示例:
文件 1:sensor.c (真正的房东)
C
// 这里是“定义” (Definition)
// 真正的分配了内存空间,并且赋值了
int Temperature = 25;文件 2:main.c (借用者)
C
// 这里是“声明” (Declaration)
// 加上 extern,告诉编译器去别处找
// 注意:千万不要在这里赋值!
extern int Temperature;
void main() {
// 现在可以直接用了
if (Temperature > 30) {
// ...
}
}3. “定义” vs “声明” (面试必考)
这是理解 extern 的关键:
- 定义 (int a = 0;):
- 动作: 像盖房子。编译器真真切切地在内存(RAM)里划了一块地皮给
a。 - 规则: 整个工程里,一个全局变量只能定义一次。如果两个文件都写
int a;,链接器会报错(重复定义)。
- 动作: 像盖房子。编译器真真切切地在内存(RAM)里划了一块地皮给
- 声明 (extern int a;):
- 动作: 像发名片。它不占内存,只是告诉编译器这个名字的类型。
- 规则: 你可以在无数个文件里写
extern int a;,只要最后能找到那个唯一的“定义”就行。
4. 工程中的“标准做法” (.h 文件)
在实际开发(如 STM32 项目)中,我们极少直接在 .c 文件里写 extern,因为太乱了。
标准做法是把 extern 放在头文件 (.h) 里。
优秀的代码结构:
1. sensor.c (源文件)
C
#include "sensor.h"
// 1. 真正的定义在这里
int Temperature = 0;
void Sensor_Init() {
Temperature = 25;
}2. sensor.h (头文件)
C
#ifndef __SENSOR_H
#define __SENSOR_H
// 2. 在头文件里统一对外声明
// 谁想用 Temperature,只要 include 这个头文件就行
extern int Temperature;
// 函数默认就是 extern 的,但写上也无妨
extern void Sensor_Init(void);
#endif3. main.c (主程序)
C
#include "sensor.h" // 3. 包含头文件
int main() {
Sensor_Init();
// 4. 因为包含了 .h,所以编译器知道 Temperature 是存在的
if (Temperature > 30) {
// ...
}
}5. extern 的三个禁忌
- 严禁在 extern 时赋值!
- 错误:
extern int a = 10; - 原因:这就变成了“定义”。如果别的文件也有
int a;,就会冲突报错。
- 错误:
- 不要滥用全局变量。
- 虽然
extern能让你访问所有变量,但如果main.c改了Temperature,sensor.c可能就乱了。 - 更好的习惯: 变量设为
static(私有),然后写一个函数Get_Temperature()供外部调用。
- 虽然
- 缺少定义。
- 如果你写了
extern int a;但整个工程没有任何一个.c文件里写了int a;。 - 结果: 编译能过(编译器信了你的鬼话),但链接时会报错(Linker Error: Undefined reference),因为链接器最后去内存里找人的时候发现根本没这个人。
- 如果你写了
总结
static:关门,自己玩(仅当前文件可见)。extern:开窗,喊话(告诉别的文件变量在这里)。- 最佳实践:在
.c中定义,在.h中用extern声明,其他文件#include那个.h。