手把手教你用C和Windows API写出专属贪吃蛇:从控制台到链表实战
发布时间:2026/10/10 19:59:08 锦皓数字建站

1. 从黑框到游戏为什么用 C Windows API 写贪吃蛇很多人学完 C 语言的指针、结构体、链表之后会陷入一种“我好像会了但又什么都写不出来”的状态。课本上的链表操作是插入一个整数、删除一个节点考试能过可一旦让你用它做点看得见的东西脑子就空了。贪吃蛇恰好是填补这个空档的最佳练手项目它足够小一个文件就能写完又足够完整涉及数据结构、输入处理、屏幕绘制、状态机、内存管理。你写完之后收获的不是“我又背了一个算法”而是“我能用 C 做出一个能玩的东西”。这篇文章聚焦 Windows 控制台环境用纯 C 语言从零实现贪吃蛇。核心检索词就是 C 语言贪吃蛇 Windows API 控制台链表实战。它适合谁适合已经学过 C 基础语法、知道指针和结构体是什么、但还没做过完整项目的同学。你不需要会图形界面不需要装额外引擎一个 gcc 或者 Visual Studio 就能跑起来。为什么强调 Windows API因为控制台默认的输入输出方式太“笨”了。你用 scanf 读方向必须按回车蛇会一顿一顿的你用 printf 画地图光标会自己往下跑屏幕疯狂闪烁。Windows 提供了一组控制台 API可以让你把光标定到任意坐标、隐藏那个闪烁的方块、直接读取键盘按键状态。掌握这几个函数你的贪吃蛇才能“跟手”。链表在这里的角色同样关键。蛇身是一串坐标长度会变化头会加、尾会减。用数组当然也能做但你要处理扩容、搬移、越界代码会变得很别扭。单链表天然适合这种“头插尾删”的队列式操作蛇头往前走一步就在链表头部插入一个新节点如果没吃到食物就把尾部节点删掉。移动、增长、碰撞检测、内存释放全都围绕这条链表展开。我试过用数组先写一版再改成链表明显感觉链表版本逻辑更干净。下面我会把整个项目拆成可复制的步骤先讲控制台初始化再定义蛇身节点和游戏状态然后写移动与碰撞逻辑最后给出编译运行和按键验证的方法。每一段代码你都可以直接贴进自己的工程里。2. 前置准备Windows API 控制台操作与开发环境在写游戏逻辑之前得先把“画布”和“输入”这两件事解决掉。控制台程序默认是一个文本流你输出什么它就往下追加什么。但游戏需要的是“在固定位置画固定符号”这就需要用到 Windows 的控制台句柄和光标定位。2.1 获取标准输出句柄Windows 把每个标准设备抽象成一个“句柄”HANDLE你可以把它理解成操作设备的遥控器。要操作控制台屏幕先拿到标准输出的句柄#include windows.h HANDLE hOutput GetStdHandle(STD_OUTPUT_HANDLE);GetStdHandle的参数有三个常用值STD_INPUT_HANDLE标准输入、STD_OUTPUT_HANDLE标准输出、STD_ERROR_HANDLE标准错误。我们画地图、画蛇用的是标准输出。2.2 隐藏光标与定位坐标控制台那个闪烁的光标在游戏里非常碍眼必须隐藏。隐藏光标需要先获取当前光标信息修改可见性再设置回去CONSOLE_CURSOR_INFO cursorInfo; GetConsoleCursorInfo(hOutput, cursorInfo); cursorInfo.bVisible FALSE; SetConsoleCursorInfo(hOutput, cursorInfo);定位坐标靠COORD结构体和SetConsoleCursorPosition。COORD里有两个SHORT成员 X 和 Y坐标系原点 (0,0) 在缓冲区左上角。封装一个设置光标的函数后面画地图会反复调用void SetPos(short x, short y) { COORD pos { x, y }; SetConsoleCursorPosition(hOutput, pos); }2.3 读取键盘按键普通getchar要等回车游戏里不能用。Windows API 提供GetAsyncKeyState传入虚拟键码返回按键状态。我们只关心“是否按下”可以宏定义一下#define KEY_PRESS(VK) ((GetAsyncKeyState(VK) 0x1) ? 1 : 0)方向键的虚拟键码是VK_UP、VK_DOWN、VK_LEFT、VK_RIGHT空格是VK_SPACEESC 是VK_ESCAPE。这样在游戏循环里轮询按键蛇就能实时响应。2.4 宽字符与本地化地图上的墙、蛇身、食物用普通字符画出来很丑。我们用□、●、★这些宽字符。宽字符在 C 里要加L前缀输出用wprintf和%lc。但直接输出会乱码必须先设置本地化#include locale.h setlocale(LC_ALL, );这一行让程序按照系统区域设置处理宽字符中文 Windows 下就能正常显示。注意setlocale要放在wprintf之前调用。2.5 开发环境与编译你可以用 Visual Studio 新建空项目把.c文件加进去也可以用 MinGW 的 gcc 命令行编译。gcc 编译命令大致是gcc snake.c -o snake.exe -lwinmm-lwinmm不是必须的但如果你后面想加音效会用到。Visual Studio 下直接 F5 运行即可。确保源码文件保存为.c而不是.cpp避免 C 名字修饰带来的链接问题。3. 可复制配置蛇身链表、游戏状态与核心结构这一节是整篇文章的骨架。我们把蛇身节点、游戏状态、方向枚举、全局句柄全部定义清楚后面写逻辑就是水到渠成。3.1 蛇身节点与链表蛇的每一节是一个坐标用单链表串起来。节点定义typedef struct SnakeNode { int x; int y; struct SnakeNode* next; } SnakeNode;蛇头就是链表头指针。移动时在头部插入新节点尾部删除节点。吃到食物就不删尾部蛇自然变长。3.2 方向与游戏状态枚举方向用枚举避免用 0/1/2/3 这种魔法数字enum DIRECTION { UP 1, DOWN, LEFT, RIGHT }; enum GAME_STATUS { OK, KILL_BY_WALL, KILL_BY_SELF, END_NORMAL };OK表示正常运行撞墙和撞自己分别对应失败状态END_NORMAL用于正常退出。3.3 游戏全局信息结构体把蛇头指针、食物坐标、方向、状态、分数、速度都放进一个结构体方便传参typedef struct SnakeGame { SnakeNode* pSnake; COORD food; enum DIRECTION dir; enum GAME_STATUS status; int score; int speed; int sleepTime; } SnakeGame;sleepTime控制每帧休眠毫秒数数值越小蛇越快。吃食物后可以适当减小实现加速。3.4 控制台初始化配置把窗口大小和标题设置好用system调用 mode 和 titlesystem(mode con cols100 lines30); system(title 贪吃蛇小游戏);然后隐藏光标、设置本地化。这几步放在GameInit里统一执行。3.5 食物生成与随机数食物坐标要随机但不能生成在墙上或蛇身上。用rand()配合time(NULL)种子srand((unsigned int)time(NULL));生成范围要避开边界比如 X 在 2 到 98 之间Y 在 2 到 28 之间。生成后遍历蛇身链表如果和某一节重合就重新生成。3.6 内存管理约定每个蛇节点用malloc分配游戏结束或重启时遍历链表free。这是 C 项目里最容易漏的地方建议写一个FreeSnake函数专门释放void FreeSnake(SnakeNode* pHead) { while (pHead) { SnakeNode* next pHead-next; free(pHead); pHead next; } }养成“谁分配谁释放”的习惯链表越长越要小心。4. 验证请求编译运行与按键响应实测代码写完之后怎么确认它真的跑起来了这一节给出完整的验证流程包括编译、运行、按键测试和成功结果说明。4.1 编译与启动把前面所有代码片段整合到一个snake.c文件里确保包含头文件#include stdio.h #include stdlib.h #include time.h #include locale.h #include windows.h用 gcc 编译gcc snake.c -o snake.exe双击snake.exe或者在命令行运行。你应该看到一个 100 列 30 行的黑框标题是“贪吃蛇小游戏”光标不闪烁地图边框用□画出蛇身是●食物是★。4.2 按键响应验证游戏循环里轮询方向键。按上、下、左、右蛇头应该立即改变方向。注意不能 180 度掉头比如正在向右走时按左键应该无效。这个判断在方向更新函数里做if (KEY_PRESS(VK_UP) game.dir ! DOWN) game.dir UP;空格键暂停再按继续。ESC 退出游戏。F3 加速F4 减速通过调整sleepTime实现。4.3 成功结果说明运行正常时你会看到蛇自动向前移动吃到食物后分数增加、蛇身变长、速度略微提升。撞到墙壁或自己的身体时游戏结束并显示得分。整个过程没有闪烁按键跟手宽字符显示正常。4.4 用 TaoToken 辅助调试与代码理解如果你在写链表移动逻辑时卡住了或者想快速理解某段 Windows API 的用法可以用 TaoToken 的模型对话功能把代码贴进去问。比如把SnakeNode的插入删除逻辑发给它让它帮你检查有没有内存泄漏。访问 https://taotoken.net/api 获取 API 接入方式或者在模型对话页面直接提问。对于这种边写边查的场景比翻文档快很多。4.5 完整移动逻辑片段蛇移动的核心是“头插尾删”。先根据当前方向计算新蛇头坐标SnakeNode* pNew (SnakeNode*)malloc(sizeof(SnakeNode)); pNew-x pSnake-x dx; pNew-y pSnake-y dy; pNew-next pSnake; pSnake pNew;然后判断是否吃到食物。如果没吃到删除尾节点if (!EatFood(pSnake, food)) { SnakeNode* cur pSnake; while (cur-next-next) cur cur-next; free(cur-next); cur-next NULL; }这段逻辑跑通蛇就能流畅移动了。5. 本篇常见错排查401、local proxy failed 与编译报错写这个项目时你大概率会遇到几类问题。有的是代码问题有的是环境问题还有的是工具链配置问题。下面按真实报错来排查。5.1 宽字符显示乱码现象地图上出现问号或者方块乱码。原因通常是setlocale没调用或者调用位置在wprintf之后。解决在main函数开头就调用setlocale(LC_ALL, )并且确保源文件保存为 GBK 或 UTF-8 with BOMVisual Studio 下推荐 GBK。5.2 光标隐藏失败现象光标还在闪。检查GetConsoleCursorInfo和SetConsoleCursorInfo是否都调用了bVisible是否设为FALSE。另外如果你在隐藏光标后又调用了system(cls)光标可能重新出现需要重新隐藏。5.3 按键无响应或连按现象按方向键没反应或者按一下蛇走好几步。GetAsyncKeyState的返回值最低位表示“自上次调用后是否按过”用 0x1判断。如果蛇走太快可能是sleepTime太小适当调大。如果按键没反应检查虚拟键码是否正确方向键是VK_UP等不是 ASCII 码。5.4 编译报错 undefined reference to GetAsyncKeyState现象gcc 链接失败。原因是没有链接 Windows 用户库。解决编译命令加上-luser32gcc snake.c -o snake.exe -luser32Visual Studio 下一般不需要手动加因为默认链接了。5.5 内存泄漏与崩溃现象游戏运行一段时间后崩溃或者退出时报错。检查FreeSnake是否在每次游戏结束和重启时调用。链表操作时删除节点前先保存next指针避免 use-after-free。5.6 使用 TaoToken 排查 API 接入问题如果你在用 TaoToken 的 API 做辅助调试时遇到 401 或者 local proxy failed先检查 API Key 是否正确、Base URL 是否填的 https://taotoken.net/api 。401 通常是 Key 无效或过期local proxy failed 一般是本地网络配置问题。可以在 API Keys 页面重新生成 Key或者查看接入文档确认请求格式。对于 Claude Code 这类工具需要同时配置 Base URL、Key 和 Model ID 三件套缺一不可。5.7 食物生成在蛇身上现象食物和蛇身重合吃不到。检查食物生成函数是否遍历了蛇身链表做碰撞判断。生成范围也要避开边界否则食物可能生成在墙上。6. 继续深入从能跑到好用到这里一个可玩的贪吃蛇已经成型了。但如果你想让它在简历上或者课程设计里更拿得出手还有几个方向可以打磨。第一是速度曲线。不要吃一个食物就固定加速可以每吃 5 个食物提升一档让难度平滑上升。第二是分数持久化用文件 IO 把最高分存到本地下次启动读取。第三是地图美化用不同颜色区分蛇头、蛇身和食物SetConsoleTextAttribute可以设置前景色和背景色。第四是暂停界面按空格时在屏幕中央显示“已暂停”而不是简单地停止循环。链表部分也可以优化。比如把蛇尾指针也保存下来删除尾节点时不用遍历O(1) 完成。或者用双向链表方便反向操作。这些改动都不大但能让你对数据结构的理解更深一层。如果你在实现过程中需要查 API 用法、让模型帮你 review 链表代码或者想试试用 AI 辅助生成测试用例可以走 TaoToken 的模型对话入口。长期做 C 项目或者 Agent 开发的话Coding Plan 会更划算。接入文档里有完整的 Base URL、Key 和 Model ID 配置说明照着填就行。最后说一个我踩过的坑不要用system(cls)每帧清屏。那样闪烁严重而且光标会重新出现。正确做法是只擦除需要更新的位置或者用双缓冲思路先画到缓冲区再一次性输出。控制台游戏虽然简单但性能优化的思路和图形程序是相通的。代码写完之后多跑几局故意撞墙、故意掉头、快速连按方向键看看有没有边界情况没处理。一个健壮的游戏是在反复折腾中磨出来的。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。