Skip to content

extern

extern 是 C 语言中用来处理**“多文件协作”**的关键工具。

如果说 static 是把变量“藏起来”(私有化),那么 extern 就是把变量**“广而告之”**(公开化)。

它的核心含义只有一句话:“这个变量(或函数)是存在的,但不是在这里定义的,去别的文件找找吧。”

我们把定义 (Definition)声明 (Declaration) 分开,就能彻底搞懂它。


1. 核心痛点:我想用隔壁文件的变量

假设你的 STM32 工程有两个文件:main.csensor.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 的关键:

  1. 定义 (int a = 0;):
    • 动作: 像盖房子。编译器真真切切地在内存(RAM)里划了一块地皮给 a
    • 规则: 整个工程里,一个全局变量只能定义一次。如果两个文件都写 int a;,链接器会报错(重复定义)。
  2. 声明 (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); 

#endif

3. main.c (主程序)

C

#include "sensor.h" // 3. 包含头文件

int main() {
    Sensor_Init();
    // 4. 因为包含了 .h,所以编译器知道 Temperature 是存在的
    if (Temperature > 30) { 
        // ...
    }
}

5. extern 的三个禁忌

  1. 严禁在 extern 时赋值!
    • 错误:extern int a = 10;
    • 原因:这就变成了“定义”。如果别的文件也有 int a;,就会冲突报错。
  2. 不要滥用全局变量。
    • 虽然 extern 能让你访问所有变量,但如果 main.c 改了 Temperaturesensor.c 可能就乱了。
    • 更好的习惯: 变量设为 static(私有),然后写一个函数 Get_Temperature() 供外部调用。
  3. 缺少定义。
    • 如果你写了 extern int a; 但整个工程没有任何一个 .c 文件里写了 int a;
    • 结果: 编译能过(编译器信了你的鬼话),但链接时会报错(Linker Error: Undefined reference),因为链接器最后去内存里找人的时候发现根本没这个人。

总结

  • static:关门,自己玩(仅当前文件可见)。
  • extern:开窗,喊话(告诉别的文件变量在这里)。
  • 最佳实践:在 .c 中定义,在 .h 中用 extern 声明,其他文件 #include 那个 .h