嵌入式开发学习日志(多文件工程、Makefile) day17 持续更新中
前言
在小型 C 语言项目中,代码量少可以全部写在一个.c文件里。当项目规模变大,全部代码写在单个源文件会造成维护困难、可读性差、不利于团队协作。这时就要使用多文件工程管理,配合Makefile工具实现项目自动化编译。本文梳理头文件、源文件分工、全局变量跨文件使用、Makefile 语法与变量相关知识点。
一、C 语言多文件工程结构
C 工程主要分为 \\头文件.h和源文件.c\\两类文件。
1. 头文件(.h)职责
头文件只做声明,不做实现
包含需要的其他头文件
数据类型声明(结构体、枚举、typedef 等)
函数声明
全局变量声明(使用
extern)宏定义
⚠️注意:头文件中不要定义函数实体、不要初始化全局变量,否则多次 include 会出现重复定义报错。
2. 源文件(.c)职责
源文件做定义与实现
包含需要的头文件(一定要包含自身对应的
.h头文件)函数的具体实现(函数体)
全局变量的定义(可以初始化)
工程一般是一对多关系:一个.h头文件,对应一个.c实现文件;多个模块就有多组 h‑c 文件。main.c 作为程序入口,包含各个模块头文件,调用模块函数。
二、全局变量跨文件使用规则
全局变量在多文件项目中极易出现重复定义错误,严格遵守下面 4 条规则:
全局变量在源文件
.c中定义(可以初始化)。如果只在定义该变量的
.c内部使用,不需要额外声明。如果要在其他外部源文件使用这个全局变量,必须做外部声明。
在对应头文件中使用
extern关键字做变量声明;extern 声明时,不能给变量初始化。
示例:
// test.c 源文件:定义全局变量 int g_num = 100; // test.h头文件:extern声明,供别的.c文件使用 extern int g_num;其他文件只要#include "test.h"就可以访问g_num。
❌错误:头文件写
extern int g_num=100;,这变成定义,会重复定义。
三、Makefile 工程编译管理
3.1 Makefile 是什么
Makefile 用于管理代码工程的编译、链接,由make工具解释执行,实现一键编译链接整个工程,不用手动敲很长 gcc 命令。
文件名可以为Makefile或者makefile。
3.2 Makefile 基础语法规则
目标文件: 依赖文件 编译规则命令 #❗前面必须是Tab键,不能用空格目标文件:想要生成的文件,比如可执行程序
a.out依赖文件:生成目标所需要的文件,一般是
.c源码编译规则:gcc 编译命令,生成目标。
示例:
a.out: main.c sum.c sub.c div.c gcc main.c sub.c sum.c div.c -I头文件路径 -L库的位置编译运行,终端直接输入命令:
makegcc 参数说明
-I(大写 i):指定头文件搜索路径-L:指定库文件搜索路径
四、Makefile 中的变量
变量可以简化 Makefile,便于后期维护,修改只需要改变量,不用修改多处编译命令。
4.1 自定义变量
定义格式:变量名 = 值,引用格式:$(变量名)。
#定义变量 OBJ = a.out SRC = main.c sum.c sub.c #引用变量 $(OBJ) 等价于 a.out变量赋值运算符:
运算符 | 作用 |
| 普通赋值,给变量赋值 |
| 追加赋值,在原有变量基础上新增内容 |
| 条件赋值:变量未赋值才赋值;已有值则保留原值 |
示例:
SRC = main.c SRC += sum.c # SRC变为 main.c sum.c4.2 Makefile 内置系统自动变量(直接使用)
自动变量 | 含义 |
| 代表当前规则的目标文件 |
| 代表所有的依赖文件 |
| 代表第一个依赖文件 |
4.3 完整 Makefile 示例
#自定义变量 OBJ = a.out SRC = main.c SRC += sum.c SRC += sub.c SRC += div.c CC = gcc INCLUDE = ../include LIBS = ../lib #编译规则 $(OBJ): $(SRC) $(CC) $^ -o $@ -I$(INCLUDE) -L$(LIBS) -lm #伪目标:清理编译产物 clean: rm $(OBJ)使用:
make:执行编译,生成 a.out
make clean:执行 clean 规则,删除可执行文件
补充:
clean没有依赖,属于伪目标,一般用来删除编译生成的产物。
五、常见踩坑总结
❌头文件写函数实现、初始化全局变量,多次
#include触发重复定义报错。 ✅头文件放声明;.c文件放实现与变量定义。❌
extern int g_val = 10;头文件 extern 声明带初始化。 ✅extern 只是声明,不能初始化;定义写在.c。❌Makefile 编译命令前面用空格,不是 Tab,直接报语法错误。 ✅规则行命令开头必须是 Tab。
❌忘记
-I指定头文件路径,头文件找不到报错。❌手动敲很长 gcc 命令,文件一多修改麻烦。 ✅用 Makefile + 变量统一管理。
小结
.h负责声明,.c负责实现;多文件工程遵循声明与实现分离。全局变量在
.c定义,.h中用 extern 做外部声明,禁止 extern 时初始化。Makefile 格式:
目标:依赖,Tab 开头写编译命令,make 命令执行。使用自定义变量简化维护,掌握
$@ $^ $<自动变量,简化编译脚本。
