将大型项目拆分成多个模块,每个模块负责特定功能。
想象你要写一个 10000 行的程序,全部放在一个文件里:
// ❌ 所有代码在一个文件
// main.c - 10000 行
// 找到 bug 试试?
模块化的好处:
// snake.h - 声明(接口)
#ifndef SNAKE_H
#define SNAKE_H
typedef struct Snake Snake;
Snake* snake_create(int x, int y);
void snake_move(Snake* s);
void snake_destroy(Snake* s);
#endif
// snake.c - 实现(具体代码)
#include "snake.h"
Snake* snake_create(int x, int y) {
// 实际实现
}
void snake_move(Snake* s) {
// 实际实现
}
防止头文件被重复包含:
// ❌ 没有保护
// 如果被包含两次,会重复定义
// ✅ 有保护
#ifndef SNAKE_H
#define SNAKE_H
// 内容只会被处理一次
#endif
src/
├── main.c # 程序入口
├── game.c / game.h # 游戏核心
├── snake.c / snake.h # 蛇模块
├── food.c / food.h # 食物模块
├── ui.c / ui.h # 界面模块
├── input.c / input.h # 输入模块
├── score.c / score.h # 分数模块
└── utils.c / utils.h # 工具函数
main.c
│
↓
game.c
↙ ↓ ↘
snake.c food.c score.c
│ │ │
└───────┴────────┘
↓
utils.c
│
↓
ui.c
│
↓
input.c
// main.c
#include <stdio.h> // 标准库
#include <stdlib.h> // 标准库
#include "game.h" // 自己的头文件
#include "snake.h"
#include "ui.h"
规则:
gcc src/main.c src/snake.c src/food.c src/ui.c -o snake_game
# 编译每个 .c 文件为 .o 文件
gcc -c src/main.c -o obj/main.o
gcc -c src/snake.c -o obj/snake.o
gcc -c src/food.c -o obj/food.o
# 链接所有 .o 文件
gcc obj/main.o obj/snake.o obj/food.o -o snake_game
#ifndef UTILS_H
#define UTILS_H
#include <stdbool.h>
// 点结构
typedef struct {
int x;
int y;
} Point;
// 函数声明
Point point_create(int x, int y);
bool point_equals(Point a, Point b);
int random_range(int min, int max);
#endif
#include "utils.h"
#include <stdlib.h>
#include <time.h>
static int seeded = 0;
Point point_create(int x, int y) {
Point p = {x, y};
return p;
}
bool point_equals(Point a, Point b) {
return a.x == b.x && a.y == b.y;
}
int random_range(int min, int max) {
if (!seeded) {
srand(time(NULL));
seeded = 1;
}
return min + rand() % (max - min + 1);
}
#ifndef SNAKE_H
#define SNAKE_H
#include "utils.h"
typedef struct Segment {
Point position;
struct Segment* next;
} Segment;
typedef struct Snake {
Segment* head;
Segment* tail;
int length;
int direction;
} Snake;
Snake* snake_create(int x, int y);
void snake_move(Snake* s);
void snake_destroy(Snake* s);
#endif
#include "snake.h"
#include <stdlib.h>
Snake* snake_create(int x, int y) {
Snake* snake = malloc(sizeof(Snake));
Segment* head = malloc(sizeof(Segment));
head->position = point_create(x, y);
head->next = NULL;
snake->head = head;
snake->tail = head;
snake->length = 1;
snake->direction = 0;
return snake;
}
void snake_move(Snake* s) {
// 实现...
}
void snake_destroy(Snake* s) {
Segment* current = s->head;
while (current) {
Segment* next = current->next;
free(current);
current = next;
}
free(s);
}
#include <stdio.h>
#include "snake.h"
#include "utils.h"
int main(void) {
Snake* snake = snake_create(10, 10);
printf("蛇创建成功!\n");
printf("位置:(%d, %d)\n",
snake->head->position.x,
snake->head->position.y);
snake_destroy(snake);
return 0;
}
// a.h
#include "b.h" // ❌ b.h 又包含 a.h
// b.h
#include "a.h" // ❌ 循环依赖!
解决: 使用前向声明
// a.h
typedef struct B B; // 前向声明,不包含 b.h
// ❌ utils.h
int counter = 0; // 每个包含的文件都会定义一次
// ✅ utils.h
extern int counter; // 声明
// ✅ utils.c
int counter = 0; // 定义
将你的贪吃蛇代码拆分成多个模块
为每个模块创建对应的 .h 和 .c 文件
使用 #ifndef 保护所有头文件
下一课:Makefile