C语言第三课:系统编程与项目实战
一、核心知识体系
1. 文件操作精要
文件处理流程
FILE *fp = fopen("data.txt", "r+"); // 读写模式打开
if(fp == NULL) {
perror("文件打开失败");
exit(EXIT_FAILURE);
}
// 文件操作代码...
fclose(fp); // 必须关闭文件
关键文件模式
模式 | 描述 | 文件存在 | 文件不存在 |
---|---|---|---|
r | 只读 | 打开 | 错误 |
w | 写入(清空内容) | 创建 | 创建 |
a | 追加写入 | 打开 | 创建 |
rb+ | 二进制读写(不截断) | 打开 | 错误 |
wb+ | 二进制读写(清空内容) | 创建 | 创建 |
文件读写函数对比
函数 | 适用类型 | 特点 | 示例 |
---|---|---|---|
fgetc/fputc | 字符 | 单字节操作 | int c = fgetc(fp); |
fgets/fputs | 字符串 | 行处理 | fgets(buf, 100, fp); |
fread/fwrite | 二进制 | 块操作 | fread(&data, sizeof(Data), 1, fp); |
fprintf/fscanf | 格式化 | 类似printf/scanf | fscanf(fp, "%d,%f", &a, &b); |
2. 预处理指令进阶
宏定义技巧
// 带参数的宏
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x)*(x))
// 调试宏
#ifdef DEBUG
#define LOG(fmt, ...) printf("[DEBUG] " fmt, ##__VA_ARGS__)
#else
#define LOG(...)
#endif
条件编译实战
#if defined(__linux__)
#include <ncurses.h>
#elif defined(_WIN32)
#include <conio.h>
#endif
// 头文件保护
#ifndef MYHEADER_H
#define MYHEADER_H
// 头文件内容
#endif
3. 多文件工程化开发
典型项目结构
snake_game/
├── Makefile # 编译脚本
├── include/
│ ├── game.h # 游戏逻辑声明
│ └── render.h # 图形渲染声明
├── src/
│ ├── main.c # 程序入口
│ ├── game.c # 游戏逻辑实现
│ └── render.c # 渲染模块实现
└── assets/ # 资源文件
头文件规范示例
// game.h
#pragma once
typedef struct {
int x;
int y;
} Position;
typedef struct {
Position body[100];
int length;
Direction dir;
} Snake;
void init_game(Snake *s);
int update_game(Snake *s);
4. 时间函数与随机数
#include <time.h>
// 初始化随机种子
srand((unsigned)time(NULL));
// 获取1-100随机数
int num = rand() % 100 + 1;
// 计算程序运行时间
clock_t start = clock();
/* 执行代码 */
double duration = (double)(clock() - start)/CLOCKS_PER_SEC;
二、贪吃蛇项目实战
1. 开发环境配置
安装ncurses库(Linux/Mac):
# Ubuntu/Debian
sudo apt-get install libncurses5-dev
# MacOS
brew install ncurses
2. 游戏架构设计
// 游戏状态结构体
typedef struct {
Snake snake;
Position food;
int score;
int game_over;
int max_x, max_y; // 屏幕尺寸
} GameState;
// 方向枚举
typedef enum { UP, DOWN, LEFT, RIGHT } Direction;
3. 核心功能实现
游戏初始化
void init_game(GameState *gs) {
initscr(); // 初始化ncurses
cbreak(); // 禁用行缓冲
noecho(); // 关闭输入回显
curs_set(0); // 隐藏光标
timeout(100); // 非阻塞输入
getmaxyx(stdscr, gs->max_y, gs->max_x);
// 初始化蛇身和食物位置...
}
食物生成算法
void generate_food(GameState *gs) {
do {
gs->food.x = rand() % (gs->max_x - 2) + 1;
gs->food.y = rand() % (gs->max_y - 2) + 1;
} while(is_position_on_snake(gs, gs->food));
}
碰撞检测逻辑
int check_collision(GameState *gs) {
Position head = gs->snake.body[0];
// 边界检测
if(head.x <= 0 || head.x >= gs->max_x-1 ||
head.y <= 0 || head.y >= gs->max_y-1)
return 1;
// 自碰撞检测
for(int i=1; i<gs->snake.length; i++) {
if(head.x == gs->snake.body[i].x &&
head.y == gs->snake.body[i].y)
return 1;
}
return 0;
}
图形渲染模块
void render_game(GameState *gs) {
clear();
// 绘制边界
box(stdscr, 0, 0);
// 绘制蛇身
for(int i=0; i<gs->snake.length; i++) {
mvaddch(gs->snake.body[i].y,
gs->snake.body[i].x,
i==0 ? '@' : '*');
}
// 绘制食物
mvaddch(gs->food.y, gs->food.x, '$');
// 显示分数
mvprintw(0, 2, " Score: %d ", gs->score);
refresh();
}
4. 游戏主循环
void game_loop() {
GameState gs;
init_game(&gs);
while(!gs.game_over) {
int ch = getch();
handle_input(&gs, ch);
if(update_game(&gs)) {
render_game(&gs);
} else {
gs.game_over = 1;
}
}
endwin(); // 结束ncurses
show_game_over(&gs);
}
三、进阶课题扩展
1. 游戏存档功能
void save_game(GameState *gs) {
FILE *fp = fopen("save.dat", "wb");
if(fp) {
fwrite(gs, sizeof(GameState), 1, fp);
fclose(fp);
}
}
int load_game(GameState *gs) {
FILE *fp = fopen("save.dat", "rb");
if(fp) {
fread(gs, sizeof(GameState), 1, fp);
fclose(fp);
return 1;
}
return 0;
}
2. 难度分级系统
typedef enum { EASY, NORMAL, HARD } Difficulty;
int get_speed(Difficulty diff) {
switch(diff) {
case EASY: return 200;
case NORMAL: return 150;
case HARD: return 100;
default: return 150;
}
}
3. 排行榜功能
typedef struct {
char name[20];
int score;
time_t timestamp;
} HighScore;
void update_leaderboard(HighScore new_score) {
// 读取现有排行榜
HighScore scores[10];
int count = 0;
FILE *fp = fopen("leaderboard.dat", "rb");
if(fp) {
count = fread(scores, sizeof(HighScore), 10, fp);
fclose(fp);
}
// 插入新记录并排序...
// 保存更新后的排行榜
}
四、实践作业
基础练习
-
实现文件加密工具:
- 使用异或运算进行简单加密
- 支持命令行参数:
./crypt input.txt output.txt key
-
开发配置文件解析器:
# config.ini [Display] width=800 height=600 fullscreen=0
项目挑战
-
完善贪吃蛇游戏:
- 添加不同皮肤选择功能
- 实现吃特殊食物加速效果
- 增加障碍物生成系统
- 支持游戏暂停/继续功能
-
开发井字棋AI:
void computer_move(char board[3][3]) { // 实现简单AI逻辑 // 1. 检查是否可以直接获胜 // 2. 阻止玩家获胜 // 3. 优先占据中心 // 4. 随机选择空位 }
五、调试与优化
性能分析工具
- 使用gprof进行性能剖析:
gcc -pg program.c -o program
./program
gprof program gmon.out > analysis.txt
- 内存检测示例:
valgrind --tool=memcheck --leak-check=full ./program
代码优化技巧
- 空间换时间策略:
// 预计算方向增量
const Position dir_delta[4] = {
{0, -1}, // UP
{0, 1}, // DOWN
{-1, 0}, // LEFT
{1, 0} // RIGHT
};
- 避免重复计算:
// 缓存边界值
int max_x = gs->max_x - 1;
int max_y = gs->max_y - 1;
- 位运算优化:
// 快速判断奇偶
if((num & 1) == 0) {
printf("偶数");
}
下节课预告:第四课将深入讲解多线程编程、网络通信基础,并使用SDL库开发2D射击游戏。