.c 文件里面:#define FLOW_EXT, .h文件里面:#ifndef FLOW_EXT #define FLOW_EXT extern #endif FLOW_EXT u16 t_boundary_1s; 怎么理解?
1. 在 .c 文件中
c
// source.c
#define FLOW_EXT // 定义 FLOW_EXT 宏(值为空)
#include "header.h"
// 此时头文件中的代码会这样展开...
2. 在 .h 文件中
h
// header.h
#ifndef FLOW_EXT // 检查 FLOW_EXT 是否未定义
#define FLOW_EXT extern // 如果未定义,定义为 "extern"
#endif
FLOW_EXT u16 t_boundary_1s; // 使用 FLOW_EXT 修饰变量
关键:展开结果不同
情况1:在定义了 FLOW_EXT 的 .c 文件中
c
// FLOW_EXT 已定义(值为空),所以跳过 #define
// 直接执行:FLOW_EXT u16 t_boundary_1s;
// 展开为:(空) u16 t_boundary_1s;
// 最终:u16 t_boundary_1s; ← 这是变量定义(分配内存)
情况2:在未定义 FLOW_EXT 的其他 .c 文件中
c
// FLOW_EXT 未定义,执行 #define FLOW_EXT extern
// 然后:FLOW_EXT u16 t_boundary_1s;
// 展开为:extern u16 t_boundary_1s; ← 这是变量声明(不分配内存)
完整示例
global.h
h
#ifndef GLOBAL_H
#define GLOBAL_H
typedef unsigned short u16;
#ifndef FLOW_EXT
#define FLOW_EXT extern
#endif
FLOW_EXT u16 t_boundary_1s; // 魔法在这里!
#endif
main.c(定义变量的文件)
c
#define FLOW_EXT // 关键:先定义这个宏
#include "global.h"
// 经过预处理后,上面那行变成:
// u16 t_boundary_1s; ← 实际定义变量,分配内存
void main() {
t_boundary_1s = 100; // 可以直接使用
}
other.c(使用变量的文件)
c
// 不定义 FLOW_EXT
#include "global.h"
// 经过预处理后,上面那行变成:
// extern u16 t_boundary_1s; ← 只是声明,不分配内存
void func() {
t_boundary_1s = 200; // 引用 main.c 中定义的变量
}
核心理解
| 文件 | FLOW_EXT 状态 | 宏展开 | 实际代码 | 作用 |
|---|---|---|---|---|
| main.c | #define FLOW_EXT |
空 | u16 t_boundary_1s; |
定义(分配内存) |
| other.c | 未定义 | extern |
extern u16 t_boundary_1s; |
声明(引用外部) |
为什么这样设计?
传统方式需要维护两处:
h
// 需要在头文件和源文件分别声明
extern u16 t_boundary_1s; // .h文件
u16 t_boundary_1s; // 某个.c文件
这种技巧的优势:
-
✅ 单一维护点:只需在头文件中写一次变量声明
-
✅ 自动区分 :通过
#define FLOW_EXT控制是定义还是声明 -
✅ 防止重复定义:只有一个文件会实际定义变量
-
✅ 代码清晰:明确哪个文件是变量的"所有者"
这是一种优雅的全局变量管理技巧,在嵌入式系统和大型C项目中很常见!