news 2026/10/21 12:46:20

C++实战:27个趣味程序从编译到二次开发

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++实战:27个趣味程序从编译到二次开发

简介:这份资源是面向C++初学者与进阶学习者的27个经典趣味程序合集,以「学练结合」的方式帮助读者在动手实践中理解语法与编程思想。内容覆盖从Hello World变体、数据类型与变量、数组与字符串处理,到指针与内存操作、类与对象、继承多态封装等面向对象核心概念,并延伸至STL容器与算法、函数重载与默认参数、异常处理、文件输入输出及模板编程等进阶主题,还包含简单游戏、数学问题求解器、文本分析工具等综合小项目。压缩包共184个文件,以28个cpp源码为核心,辅以dsp、dsw、ncb、opt、plg等Visual Studio工程配置与编译中间文件,另有19个doc说明文档、6个txt与1个dat数据文件,整体约222KB,结构紧凑便于逐项研读。目前已有174人学习关注,适合希望系统巩固C++基础、通过阅读与调试真实代码提升实战能力的编程爱好者。

1. 从一堆“玩具代码”里挖出 C++ 的底层逻辑

很多人学 C++ 卡在同一个地方:语法书翻完了,指针、引用、类、模板都认识,但真让自己写点东西,脑子里一片空白。这个压缩包里塞了 27 个经典趣味程序,从猜数字、贪吃蛇到简易计算器、学生成绩管理,全是那种“看着简单、动手就翻车”的小项目。它适合两类人:一是刚学完 C++ 基础语法、急需用完整代码把知识点串起来的新手;二是想拿几个轻量级案例练手、熟悉控制台交互和文件读写的老手。每个程序都不长,但麻雀虽小五脏俱全,编译、调试、改参数、加功能,一套流程走下来,比看十遍教材都管用。下面我就按“能跑、能看懂、能改”的顺序,把这包代码拆开讲透。

2. 环境准备与项目结构:先把第一个程序跑起来

2.1 编译器选择与最小验证

拿到压缩包先别急着解压一堆文件,第一步是确认你机器上有没有能编译 C++ 的环境。Windows 下最省事的是装个 MinGW 或者直接用 Visual Studio 的开发者命令提示符;macOS 装 Xcode Command Line Tools 就行;Linux 一般自带 g++。验证方法很简单,终端里敲:

g++ --version

能打印出版本号就说明编译器就位。如果提示找不到命令,Windows 用户检查环境变量 Path 里有没有 MinGW 的 bin 目录,macOS 用户跑一下xcode-select --install。这一步看着基础,但我见过太多人卡在这儿,后面所有操作都白搭。

2.2 目录结构与文件分类

解压之后你会看到一堆.cpp文件,可能还有几个.h头文件和.txt数据文件。常见做法是按功能分个类,比如:

27个经典趣味C++程序/ ├── 01_guess_number.cpp ├── 02_snake_game.cpp ├── 03_student_manager.cpp ├── ... ├── data/ │ └── scores.txt └── README.md

有些程序是单文件就能跑的,比如猜数字、九九乘法表;有些带文件读写的,比如学生成绩管理,会依赖data/目录下的文本文件。我一般会先扫一眼每个文件开头的注释,看看有没有#include之外的特殊依赖,比如<graphics.h>这种老式图形库——如果有,那这个程序可能得在特定环境下才能编译,先跳过,别一上来就啃硬骨头。

2.3 编译与运行第一个程序

挑一个最简单的,比如猜数字。假设文件叫guess.cpp,编译命令:

g++ guess.cpp -o guess ./guess

Windows 下生成的是guess.exe,直接双击或者在命令行里guess.exe运行。如果编译报错,先看错误信息第一行,通常是缺头文件或者拼写错误。比如#include <iostream>写成了<iostream.h>,老编译器可能认,新编译器直接报错。改过来就行。

提示:编译时加-Wall参数能打开所有警告,很多潜在问题会在编译阶段就暴露出来,别等到运行时才抓瞎。

3. 核心模块拆解:从猜数字到贪吃蛇的代码骨架

3.1 猜数字:随机数与循环控制

猜数字这个程序虽然简单,但它把 C++ 的几个核心点全串起来了:随机数生成、循环、条件判断、输入输出。看一段典型实现:

#include <iostream> #include <cstdlib> #include <ctime> int main() { // 用当前时间做种子,保证每次运行随机数不同 srand(static_cast<unsigned int>(time(nullptr))); int target = rand() % 100 + 1; // 生成 1~100 的随机数 int guess = 0; int tries = 0; std::cout << "猜一个 1 到 100 之间的数字:" << std::endl; while (guess != target) { std::cin >> guess; tries++; if (guess > target) { std::cout << "大了,再试试:" << std::endl; } else if (guess < target) { std::cout << "小了,再试试:" << std::endl; } else { std::cout << "猜对了!用了 " << tries << " 次。" << std::endl; } } return 0; }

这里的关键是srand和rand的配合。srand只调用一次,放在循环外面;rand() % 100 + 1控制范围。很多人会把srand放进循环里,结果每次生成的随机数都一样,这就是典型的“玄学 bug”——代码看着没问题,跑起来就是不对。参数方面,% 100决定上限,+ 1决定下限,想改成 1 到 1000 就写% 1000 + 1。

3.2 贪吃蛇:控制台绘图与键盘响应

贪吃蛇稍微复杂一点,涉及控制台光标控制、非阻塞键盘输入和二维数组。Windows 下常用<conio.h>里的_kbhit()和_getch(),Linux 下得用<termios.h>改终端模式。看一段 Windows 风格的骨架:

#include <iostream> #include <conio.h> #include <windows.h> const int WIDTH = 20; const int HEIGHT = 15; int snakeX[100], snakeY[100]; // 蛇身坐标数组 int snakeLen = 3; // 初始长度 int foodX, foodY; int dir = 0; // 0上 1下 2左 3右 void gotoxy(int x, int y) { COORD coord = {static_cast<SHORT>(x), static_cast<SHORT>(y)}; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); } void draw() { gotoxy(0, 0); for (int i = 0; i < HEIGHT; i++) { for (int j = 0; j < WIDTH; j++) { bool isSnake = false; for (int k = 0; k < snakeLen; k++) { if (snakeX[k] == j && snakeY[k] == i) { std::cout << "O"; isSnake = true; break; } } if (!isSnake) { if (j == foodX && i == foodY) std::cout << "*"; else std::cout << " "; } } std::cout << std::endl; } }

这段代码的核心是gotoxy函数,它把光标移到指定位置再输出,避免整屏刷新导致的闪烁。snakeX和snakeY数组存蛇身每一节的坐标,snakeLen控制长度。键盘响应部分用_kbhit()检测有没有按键,再用_getch()读取键值,根据键值改dir变量。常见坑是方向键的键值在不同编译器下不一样,得实际打印出来确认。

3.3 学生成绩管理:结构体与文件读写

学生成绩管理是这包里比较“正经”的项目,涉及结构体定义、动态数组和文件持久化。看一段典型实现:

#include <iostream> #include <fstream> #include <vector> #include <string> struct Student { std::string name; int id; float score; }; void saveToFile(const std::vector<Student>& students, const std::string& filename) { std::ofstream out(filename); if (!out) { std::cerr << "无法打开文件:" << filename << std::endl; return; } for (const auto& s : students) { out << s.id << " " << s.name << " " << s.score << std::endl; } out.close(); } void loadFromFile(std::vector<Student>& students, const std::string& filename) { std::ifstream in(filename); if (!in) return; Student s; while (in >> s.id >> s.name >> s.score) { students.push_back(s); } in.close(); }

这里用std::vector存学生数据,比固定数组灵活。saveToFile把每个学生的学号、姓名、成绩按空格分隔写入文本文件,loadFromFile反过来读。注意while (in >> ...)的写法,它会自动跳过空白字符,读到文件末尾就停止。常见问题是文件路径写错,比如data/scores.txt在程序运行时的工作目录下不存在,就会静默失败。我一般会在打开文件后加个if (!out)判断,至少能打印个错误提示。

4. 避坑与排查:那些年我们踩过的编译和运行坑

4.1 中文乱码:源文件编码与控制台编码不一致

现象:程序里写的中文提示,运行出来全是乱码。原因通常是源文件保存成了 UTF-8,但 Windows 控制台默认用 GBK 解码。解决办法有两个:一是把源文件另存为 GBK 编码;二是在程序开头加system("chcp 65001");把控制台切到 UTF-8。我一般用第二种,跨平台也方便。

4.2 随机数不随机:srand 调用位置错误

现象:每次运行猜数字,目标数都一样。原因就是srand被放进了循环或者每次调用rand前都调了一次。正确做法是程序启动时调一次srand(time(nullptr)),之后只调rand()。这个坑几乎每个新手都会踩一遍。

4.3 文件读写失败:工作目录与相对路径

现象:学生成绩管理程序运行后,保存的数据下次打开就没了。原因是程序的工作目录不是源码所在目录,data/scores.txt实际被写到了别的地方。解决办法是用绝对路径,或者在 IDE 里设置工作目录。命令行下运行的话,先cd到程序所在目录再执行。

4.4 控制台闪退:程序跑完窗口就关

现象:双击.exe运行,窗口一闪而过。原因是main函数执行完就返回了,控制台自动关闭。解决办法是在return 0;前面加system("pause");或者std::cin.get();。前者是 Windows 专用,后者跨平台。我一般用std::cin.get();,干净一些。

4.5 头文件找不到:老式库与新编译器不兼容

现象:编译贪吃蛇时提示conio.h找不到。原因是conio.h是 DOS 时代的老库,Linux 和 macOS 默认没有。解决办法是换用<termios.h>自己实现键盘检测,或者干脆在 Windows 下用 Visual Studio 编译。如果只是学习,建议先跑通 Windows 版本,再考虑跨平台移植。

5. 二次开发与改造:从能跑到好用

5.1 给猜数字加难度选择和排行榜

原版猜数字只有 1 到 100 一个难度,改起来很简单。加个菜单让用户选范围,再把每次的成绩写进文件:

void playGame(int maxNum) { int target = rand() % maxNum + 1; int guess, tries = 0; while (true) { std::cin >> guess; tries++; if (guess == target) break; std::cout << (guess > target ? "大了" : "小了") << std::endl; } std::cout << "用了 " << tries << " 次" << std::endl; // 追加到排行榜文件 std::ofstream out("rank.txt", std::ios::app); out << tries << std::endl; }

std::ios::app是追加模式,不会覆盖之前的数据。排行榜读取时把数字读进vector,排序后取前五名就行。这个改造练的是函数封装和文件追加,难度不大但很实用。

5.2 贪吃蛇加穿墙和加速道具

原版贪吃蛇撞墙就死,可以改成穿墙:当蛇头坐标超出边界时,取模绕回来。加速道具就是在食物数组里加个类型标记,吃到加速道具时把游戏循环的延时从 200ms 降到 100ms。核心改动在碰撞检测和食物生成逻辑里,代码量不大,但能让可玩性提升一个档次。

5.3 学生成绩管理换用 SQLite 存储

文本文件存数据,查询和统计都不方便。换成 SQLite 只需要引入一个头文件和一个.c文件,然后把saveToFile和loadFromFile换成 SQL 语句:

sqlite3* db; sqlite3_open("students.db", &db); std::string sql = "INSERT INTO students (id, name, score) VALUES (?, ?, ?);"; sqlite3_stmt* stmt; sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr); sqlite3_bind_int(stmt, 1, s.id); sqlite3_bind_text(stmt, 2, s.name.c_str(), -1, SQLITE_STATIC); sqlite3_bind_double(stmt, 3, s.score); sqlite3_step(stmt); sqlite3_finalize(stmt);

这样改完之后,按成绩排序、按姓名模糊查询都是一条 SQL 的事。SQLite 的集成成本很低,适合拿来练手数据库操作。

5.4 用 CMake 统一管理多个程序

27 个程序如果每个都手动敲g++命令,效率太低。写个CMakeLists.txt:

cmake_minimum_required(VERSION 3.10) project(FunCpp) set(CMAKE_CXX_STANDARD 17) file(GLOB SOURCES "*.cpp") foreach(src ${SOURCES}) get_filename_component(name ${src} NAME_WE) add_executable(${name} ${src}) endforeach()

然后mkdir build && cd build && cmake .. && make,所有程序一次性编译完。这个改造适合想批量测试代码的场景,也顺便练了 CMake 的基本用法。

5.5 改造验证:怎么确认没改坏

每次改完代码,先编译看有没有警告,再跑一遍核心流程。比如猜数字改完难度选择后,分别选 1 到 10 和 1 到 1000 各跑一次,确认随机数范围正确。贪吃蛇改完穿墙后,故意往边界撞几次,看能不能绕回来。学生成绩管理改完 SQLite 后,先插几条数据,再查出来对比。我一般会写个简单的测试脚本,把输入输出重定向到文件,跑完 diff 一下,省得每次手动敲。

从那以后我每次拿到这种“玩具代码”包,都强制自己先跑通一个最简单的,再按模块逐个拆解,最后挑一个动手改。改坏了就回退,改好了就记下来。这套流程走下来,C++ 的底子才算真正打牢。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 12:46:09

CycleGAN无配对图像翻译:循环一致性损失与Keras实现避坑指南

简介&#xff1a;这份资源是CycleGAN的Keras实现代码包&#xff0c;面向具备一定深度学习基础、希望上手无监督图像到图像转换的开发者与研究者。CycleGAN通过两个生成器与两个判别器的对抗博弈&#xff0c;配合循环一致性损失&#xff0c;在无需成对样本的条件下完成跨域风格迁…

作者头像 李华
网站建设 2026/10/21 12:45:53

改进DETR算法的齿轮表面缺陷识别方法与工程实践

齿轮表面缺陷检测这个方向&#xff0c;前几年大家讨论最多的是YOLO改到第几版、mAP能到多少。这两年风向变了&#xff0c;越来越多实验室和工厂技术团队在往DETR这类端到端检测器上转。原因也不难理解&#xff1a;齿轮缺陷的形状差异实在太大&#xff0c;一条划痕可以横跨整个齿…

作者头像 李华
网站建设 2026/10/21 12:45:49

self_operating_computer实战:多模态大模型操控电脑的完整指南

第一次接触 self_operating_computer 时&#xff0c;我第一反应是&#xff1a;“这不就是给大模型装上了一双眼睛和一只手吗&#xff1f;”它要做的事情其实很直白&#xff1a;让 AI 看着电脑屏幕&#xff0c;自己移动鼠标、敲击键盘&#xff0c;像真人一样完成你交代的任务。…

作者头像 李华
网站建设 2026/10/21 12:45:03

LDA主题模型从原理到实战:吉布斯采样与中文文本挖掘

第一次翻开《统计学习方法》的LDA章节&#xff0c;我第一反应是把书合上&#xff0c;去窗边喘口气。前面十几章虽然也劝退&#xff0c;但至少每章都在告诉你"我要拟合一个分类器"或者"我要估计一个序列标注"&#xff0c;而LDA&#xff08;潜在狄利克雷分配…

作者头像 李华
网站建设 2026/10/21 12:44:34

Flutter折叠屏适配:基于Cassowary约束求解的响应式布局引擎

去年做折叠屏适配的时候&#xff0c;我几乎被 Flutter 里那套布局原语逼到墙角。MediaQuery 拿到的宽度一变&#xff0c;所有 Row、Column 的比例都得重新算&#xff0c;代码里密密麻麻全是物理像素的分支判断。后来我把 cassowary 这个线性约束求解器搬进了布局层&#xff0c;…

作者头像 李华
网站建设 2026/10/21 12:44:20

Druid多数据源动态切换:连接池与路由机制实战解析

做后端开发这些年&#xff0c;连接池切换这套东西我踩过不少坑。最近在改一个报表项目&#xff0c;同一个接口要根据传入的租户标识落到不同的业务库&#xff0c;刚开始图省事&#xff0c;在代码里写死两个数据源&#xff0c;需要哪个库就把DataSource对象传来传去。没过几轮&a…

作者头像 李华