学会 C 语言程序的调试方法,掌握常见问题排查技巧。
# Makefile
CFLAGS = -Wall -Wextra -std=c99
警告选项:
| 选项 | 含义 | 作用 |
|——|——|——|
| -Wall | Warning All | 显示所有警告 |
| -Wextra | Warning Extra | 显示额外警告 |
| -Werror | Warning Error | 把警告当错误 |
警告 1:未使用变量
warning: unused variable 'x'
// 解决:删除或使用该变量
int x = 5; // ❌
printf("%d", x); // ✅
警告 2:隐式函数声明
warning: implicit declaration of function 'foo'
// 解决:添加函数声明或包含头文件
void foo(void); // 声明
#include "foo.h" // 或包含头文件
警告 3:类型不匹配
warning: comparison between signed and unsigned
// 解决:统一类型
int x = -1;
unsigned int y = 5;
if (x < y) ... // ❌ 警告
int x = -1;
int y = 5;
if (x < y) ... // ✅
printf("DEBUG: x = %d\n", x);
printf("DEBUG: 位置 (%d, %d)\n", game->current.x, game->current.y);
1. 标记代码执行位置
printf("=== 进入 game_update() ===\n");
// ... 代码
printf("=== 离开 game_update() ===\n");
2. 打印关键变量
printf("分数:%d, 等级:%d, 行数:%d\n",
game->score, game->level, game->lines_cleared);
3. 条件打印
if (game->game_over) {
printf("游戏结束!最终分数:%d\n", game->score);
}
4. 调试碰撞检测
bool check_collision(Tetromino* t, Board* board) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (t->shape[i][j]) {
int x = t->x + j;
int y = t->y + i;
printf("检查 (%d,%d) 形状 [%d][%d]=%d\n",
x, y, i, j, t->shape[i][j]);
}
}
}
}
症状: 方块显示为 `` 或其他乱码
原因: 使用了中文字符或 Unicode
解决:
// ❌ 错误
mvaddstr(y, x, "██");
// ✅ 正确
mvaddch(y, x * 2, ACS_CKBOARD);
mvaddch(y, x * 2 + 1, ACS_CKBOARD);
症状: 方块瞬间到底,无法控制
原因: drop_interval 设置太小
解决:
// game_init() 中
game->drop_interval = 3000; // 3 秒/格
// 不是 100 或 500
症状: 方块穿墙或穿过其他方块
排查步骤:
// 1. 添加调试输出
printf("方块位置:(%d, %d)\n", game->current.x, game->current.y);
printf("棋盘 [%d][%d] = %d\n", y, x, board->cells[y][x]);
// 2. 检查坐标计算
int x = t->x + j; // 确认 t->x 正确
int y = t->y + i; // 确认 t->y 正确
// 3. 检查边界条件
if (x < 0) printf("碰撞:左边界\n");
if (x >= BOARD_WIDTH) printf("碰撞:右边界\n");
检测工具:valgrind
# 安装
sudo apt install valgrind
# 运行
valgrind --leak-check=full ./tetris
# 输出示例
==1234== 40 bytes in 1 blocks are definitely lost
==1234== at 0x4C2FB55: malloc (in ...)
==1234== by 0x400ABC: game_create (game.c:10)
解决: 确保每个 malloc 都有对应的 free
Game* game = malloc(sizeof(Game));
// ... 使用
free(game); // 别忘了!
# 调试版本
debug: CFLAGS += -g -DDEBUG
debug: clean all
# 发布版本
release: CFLAGS += -O2 -DNDEBUG
release: clean all
#ifdef DEBUG
printf("DEBUG: 详细输出\n");
#endif
// 使用
make debug // 编译调试版本,有调试输出
make release // 编译发布版本,无调试输出
// 问题:游戏运行不正常
// 方法:分段测试
// 1. 测试初始化
game_init(game);
printf("初始化完成\n");
getch(); // 暂停,检查状态
// 2. 测试渲染
game_render(game);
printf("渲染完成\n");
getch();
// 3. 测试输入
int key = input_get_key();
printf("按键:%d\n", key);
// 创建最小测试程序
#include "tetromino.h"
int main() {
Tetromino t;
tetromino_init(&t, TETRO_I);
printf("方块初始化成功\n");
printf("位置:(%d, %d)\n", t.x, t.y);
return 0;
}
// 编译测试
gcc -Isrc src/tetromino.c test.c -o test
./test
在碰撞检测函数中添加调试输出
使用 valgrind 检查是否有内存泄漏
创建一个最小测试程序,只测试方块旋转功能
下一课:影子方块