翻译单元
简介
翻译单元(Translation Unit, 简称 TU) 是编译器进行独立编译的最小代码单元 —— 简单说,它是编译器处理代码时的 “基本工作单位”,最终会被编译成一个
.o(目标文件)多个目标文件再通过链接器合并成可执行文件(如
.exe)或库文件(如.a/.so)。
简单来说:翻译单元就是 一个.c文件+其递归包含的头文件(头文件中的头文件) 进行预处理后的代码文本
重复定义
关键特性: 翻译单元之间是 “独立的”
每个翻译单元的编译过程完全独立,编译器处理
a.c时,完全不知道b.c的存在 !这是理解重复定义问题的关键所在
举个直观例子:有 a.c 和 b.c 两个源文件,各自形成独立的翻译单元:
a.c的翻译单元:a.c+ 它包含的所有头文件(如stdio.h、utils.h);b.c的翻译单元:b.c+ 它包含的所有头文件(可能和a.c有重叠,也可能不同);- 编译时,
a.c生成a.o,b.c生成b.o,两者互不干扰; - 只有到链接阶段,链接器才会把
a.o、b.o以及依赖的库文件(如 HAL 库的.a文件)合并成最终的可执行文件(如stm32_app.elf)。
当多个翻译单元包含相同头文件时,可能会出现 “重复定义” 链接错误,当每个 .o 都有同名的全局实体定义(如全局变量、非内联函数等),链接时冲突
注意:
头文件保护宏只能防止 “同一翻译单元内的重复包含”,无法解决多个翻译单元的重复定义
如何避免重复定义
[!订单] 声明与定义
- 声明(Declaration):告诉编译器 “有一个函数 / 变量存在”,但不提供具体实现或内存分配(相当于 “给个名字和类型,让编译器认识它”);
- 定义(Definition):给函数 / 变量分配内存、提供具体实现(相当于 “真的创建这个东西,让它能被使用”);
- 变量的赋值和函数功能的实现属于 “定义” 的一部分
宏定义
#define:宏的本质是 “文本替换”,全程不涉及 “编译 / 链接”,也不分配内存、不生成代码,所以在头文件进行宏定义,是不出现重复定义的。
声明与定义分离!!!
- 头文件中绝不能进行 无
static的变量定义 - 头文件中绝对不能写 无
static的函数实现
使用static关键字 ---(作用域隔离)
如果一定要在头文件中进行声明与定义,使用static关键字
关键字static 会把实体的 “链接属性” 从「外部链接」改成了「内部链接」 —— 每个 .c 文件(翻译单元)里的同名静态实体,会被编译器视为「完全独立的个体」,编译后只在自己的 .o 目标文件中可见,链接时不会互相干扰。
[! 注意] 当要跨文件访问全局变量时,一般不会使用
static在.h文件中定义变量 而是在.c文件定义后,在对应的.h文件中用extern声明该变量