源文件增多后,手工重复输入编译与链接命令既繁琐又易遗漏已修改的文件。Makefile 描述目标与依赖关系,由 make 按规则执行命令,并支持仅重建发生变更的部分(增量编译)。

Makefile 的作用

Makefile 供 make 读取,每条规则说明如何从依赖文件生成目标文件。Linux 环境常预装 make;其他平台可安装 GNU Make 等实现。

规则结构

target: dependencies
	command
  • target:要生成的文件(如 main.o、可执行文件 main)。
  • dependencies:生成 target 所需的输入文件。
  • command:执行的 shell 命令;必须以 Tab 字符缩进,不能用空格代替。

可用变量简化重复,以 $() 引用:

CC = gcc
$(CC) -o main main.o

多文件工程示例

假设存在 main.c、multiply.c、factorial.c 及头文件 function.h:

/* function.h */
#ifndef FUNCTION_H
#define FUNCTION_H

int multiply(int, int);
int factorial(int);
void print(void);

#endif
/* multiply.c */
#include "function.h"

int multiply(int a, int b) { return a * b; }
/* factorial.c */
#include "function.h"

int factorial(int n) {
    if (n == 1)
        return 1;
    return n * factorial(n - 1);
}
/* main.c */
#include <stdio.h>
#include "function.h"

int main(void) {
    printf("%d\n", multiply(2, 3));
    printf("%d\n", factorial(5));
    print();
    return 0;
}

手工编译步骤示例:

gcc -c main.c
gcc -c multiply.c
gcc -c factorial.c
gcc -o main main.o multiply.o factorial.o
./main

对应的 Makefile 示例:

# 编译器
CC = gcc

# 编译选项:开全警告、带调试信息
CFLAGS = -Wall -g

# 目标可执行文件
TARGET = main

# 源文件
SRCS = main.c multiply.c factorial.c

# 把 .c 换成 .o
OBJS = $(SRCS:.c=.o)

# 默认目标:编译并运行
all: $(TARGET) run

# 链接:把 .o 合成可执行文件
$(TARGET): $(OBJS)
	$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)

# 通用规则:任何 .o 由同名 .c 编译而来
%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

# 运行
run: $(TARGET)
	./$(TARGET)

# 清理生成的文件
clean:
	del $(TARGET).exe $(OBJS)

在项目目录执行:

make

若仅修改 multiply.c,再次执行 make 时通常只重新编译 multiply.o 并重新链接,其余目标文件可复用。清理产物:

make clean

使用要点

  • Tab 缩进:命令行必须以 Tab 开头。
  • 自动变量:$@ 表示当前目标,$< 表示首个依赖,$^ 表示全部依赖。
  • 默认目标:文件中第一条规则在无参数执行 make 时被构建。
  • 目录组织:头文件、源文件与生成物分目录存放,便于维护较大工程。

本节介绍了 Makefile 的基本结构与增量编译思路。下一节说明 printf / scanf 格式说明符的用法与排版修饰。