C语言学习(Makefile)
Jun 01. 2026

AI文摘
此内容由AI根据文章内容自动生成.
加载中。

Makefile 是 Linux/Unix 环境下用于管理项目编译和构建的核心工具。它的核心价值在于自动化增量编译(即只重新编译修改过的文件,大幅提升大型项目的构建效率)。

1. Makefile 的核心三要素

Makefile 的基本结构由“目标”、“依赖”和“命令”组成。

  • 目标 (Target):要生成的文件(如可执行文件、.o 目标文件)或要执行的动作(如 clean)。
  • 依赖 (Prerequisites):生成目标所需要的源文件或其他目标。
  • 命令 (Commands):生成目标的具体操作(注意:必须以 Tab 键开头,不能用空格)。

基础示例: 假设有一个 hello.c 文件,要编译生成 hello 可执行程序:

# 目标: 依赖
hello: hello.c
    gcc hello.c -o hello  # 命令前必须按 Tab 键

# 清理生成的文件(伪目标)
clean:
    rm -f hello

在终端输入 make 即可自动执行编译,输入 make clean 则会删除生成的可执行文件。

2. 变量与自动变量

为了减少重复代码,提高可维护性,Makefile 支持自定义变量和内置的自动变量。

  • 自定义变量:如 CC (编译器)、CFLAGS (编译选项)、TARGET (目标名)。
  • 自动变量
    • $@:表示当前规则的目标文件。
    • $^:表示当前规则的所有依赖文件。
    • $<:表示当前规则的第一个依赖文件。

变量使用示例:

# 定义变量
CC := gcc
CFLAGS := -Wall -g
TARGET := hello

$(TARGET): hello.c
    $(CC) $(CFLAGS) -o $@ $^  # 等价于 gcc -Wall -g -o hello hello.c

.PHONY: clean
clean:
    rm -f $(TARGET)

3. 模式规则与通配符函数

在多文件项目中,为每个 .c 文件单独写编译规则非常繁琐。通过模式规则(用 % 匹配)和通配符函数,可以极大简化 Makefile。

  • $(wildcard *.c):动态获取当前目录下所有 .c 源文件。
  • $(patsubst %.c,%.o,$(SRCS)):将 .c 文件列表替换为 .o 文件列表。
  • %.o: %.c:定义通用的 .o 文件生成规则。

多文件项目通用模板示例: 假设项目中有 main.cfunc.c 等多个源文件:

CC := gcc
CFLAGS := -Wall -g
TARGET := app

# 自动获取所有 .c 文件
SRCS := $(wildcard *.c)
# 将 .c 替换为 .o
OBJS := $(patsubst %.c,%.o,$(SRCS))

$(TARGET): $(OBJS)
    $(CC) $(CFLAGS) -o $@ $^

# 一条规则匹配所有 .o 文件的生成
%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@

.PHONY: clean
clean:
    rm -f $(TARGET) $(OBJS)

4. 伪目标 (.PHONY)

伪目标不代表实际的文件名,而是一个动作标识符。使用 .PHONY 声明伪目标,可以避免与同名文件冲突,并强制 make 每次都执行该命令(跳过时间戳对比)。 例如,如果目录下恰好有一个叫 clean 的文件,若不使用 .PHONY: clean,执行 make clean 时会提示“clean已是最新”而拒绝执行删除操作。

伪目标示例:

# 声明 clean 和 install 为伪目标
.PHONY: clean install

clean:
    rm -f $(TARGET) $(OBJS)

install:
    cp $(TARGET) /usr/local/bin/

5. 自动依赖生成(解决头文件依赖)

普通的 Makefile 只能监控 .c 文件的修改。如果修改了 .h 头文件,make 往往无法感知并重新编译。可以通过编译器选项(如 -MMD -MP)自动生成依赖文件(.d 文件)来解决这个问题。

自动依赖生成示例:

CC := gcc
CFLAGS := -Wall -g -MMD -MP  # -MMD生成依赖,-MP防止头文件删除后报错
TARGET := app
SRCS := $(wildcard *.c)
OBJS := $(patsubst %.c,%.o,$(SRCS))
DEPS := $(OBJS:.o=.d)        # 获取所有的 .d 依赖文件

$(TARGET): $(OBJS)
    $(CC) $(CFLAGS) -o $@ $^

%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@

# 包含自动生成的依赖文件(- 表示如果文件不存在也不报错)
-include $(DEPS)

.PHONY: clean
clean:
    rm -f $(TARGET) $(OBJS) $(DEPS)
> comment on / twitter
>
CC BY-NC-SA 4.0 2021-PRESENT © RYANUO