欢迎光临
我们一直在努力

C学习教程,从入门到精通,C语言单词背记闯关游戏 — 核心知识点详解(17)

C语言单词背记闯关游戏 — 核心知识点详解


一、预处理模块设计

1.1 文件包含指令 #include

知识点说明:

#include 是C语言的预处理指令,用于在编译之前将指定的头文件内容插入到当前源文件中。头文件中包含了标准库函数的声明、宏定义、类型定义等内容。

两种形式:

#include <stdio.h> // 从系统标准库路径中查找头文件
#include "myheader.h" // 先从当前目录查找,再从系统路径查找

常用头文件:

头文件用途
stdio.h 标准输入输出函数(printf, scanf, fopen等)
stdlib.h 通用工具函数(system, exit, rand, srand等)
string.h 字符串处理函数(strcmp, strcpy, strlen等)
time.h 时间相关函数(time, clock等)
conio.h 控制台输入输出(getch, kbhit等,Windows特有)
windows.h Windows API函数(光标定位等)

1.2 宏定义 #define

知识点说明:

#define 用于定义宏常量和宏函数。在预处理阶段,编译器会将所有宏名替换为对应的值或表达式。

无参宏(宏常量):

#define 标识符 替换文本

  • 宏名通常用大写字母,以区分变量
  • 宏定义末尾不加分号
  • 宏定义是简单的文本替换,不进行类型检查

带参宏(宏函数):

#define 标识符(参数列表) 替换文本

  • 参数和整个表达式建议加括号,避免运算优先级问题
  • 带参宏在预处理阶段展开,不涉及函数调用开销

入门案例:

#include <stdio.h>

// 定义宏常量:游戏总关数
#define TOTAL_ROUNDS 5

// 定义宏常量:每关单词数量
#define WORDS_PER_ROUND 4

// 定义宏常量:答对一题的得分
#define SCORE_PER_WORD 20

// 带参宏:计算某一关的满分(括号防止优先级错误)
#define ROUND_FULL_SCORE(n) ((n) * SCORE_PER_WORD)

int main() {
// 使用宏常量
int rounds = TOTAL_ROUNDS; // 预处理后替换为:int rounds = 5;
int words = WORDS_PER_ROUND; // 预处理后替换为:int words = 4;

// 使用带参宏
int fullScore = ROUND_FULL_SCORE(WORDS_PER_ROUND); // 替换为:((4) * 20)

printf("游戏总关数: %d\\n", rounds); // 输出:游戏总关数: 5
printf("每关单词数: %d\\n", words); // 输出:每关单词数: 4
printf("每关满分: %d 分\\n", fullScore); // 输出:每关满分: 80 分

return 0; // 程序正常结束
}


1.3 条件编译指令

知识点说明:

条件编译允许根据条件决定哪些代码参与编译,哪些代码被忽略。常用于防止头文件重复包含、跨平台代码适配等场景。

防止头文件重复包含:

#ifndef 标识符 // 如果没有定义该标识符
#define 标识符 // 则定义它
// 头文件内容
#endif // 条件编译结束

执行逻辑:

  • 第一次包含时,标识符未定义 → 进入 #ifndef 块 → 执行 #define 和头文件内容
  • 第二次包含时,标识符已定义 → 跳过整个块 → 避免重复定义

  • 二、游戏开始界面显示

    2.1 格式化输出函数 printf

    知识点说明:

    printf 是C语言中最常用的输出函数,定义在 stdio.h 中,用于将格式化的数据输出到标准输出设备(通常是屏幕)。

    函数原型:

    int printf(const char *format, ...);

    • format:格式控制字符串,包含普通字符和格式说明符
    • …:可变参数,对应格式说明符的值
    • 返回值:成功输出的字符总数

    常用格式说明符:

    格式符含义示例
    %d 十进制整数 printf("%d", 42) → 42
    %f 浮点数(默认6位小数) printf("%f", 3.14) → 3.140000
    %.2f 保留2位小数 printf("%.2f", 3.14) → 3.14
    %c 单个字符 printf("%c", 'A') → A
    %s 字符串 printf("%s", "hello") → hello
    %5d 右对齐,宽度5 printf("%5d", 42) → 42
    %-5d 左对齐,宽度5 printf("%-5d", 42) → 42

    转义字符:

    转义字符含义
    \\n 换行
    \\t 水平制表符(Tab)
    \\\\ 反斜杠本身
    \\" 双引号本身
    \\0 空字符(字符串结束标志)

    2.2 控制台光标定位(Windows平台)

    知识点说明:

    在Windows控制台程序中,可以通过Windows API函数 SetConsoleCursorPosition 将光标移动到指定位置,从而在屏幕任意位置输出内容,实现界面的精确定位显示。

    关键结构体和函数:

    // COORD 结构体定义(在 windows.h 中)
    typedef struct {
    SHORT X; // 列号(从0开始)
    SHORT Y; // 行号(从0开始
    } COORD;

    // 获取标准输出句柄
    HANDLE GetStdHandle(DWORD nStdHandle);

    // 设置光标位置
    BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput, COORD dwCursorPosition);

    自定义光标定位函数封装:

    #include <windows.h>

    // 函数功能:将控制台光标移动到(x, y)位置
    // 参数 x:列号(水平方向,从左到右递增)
    // 参数 y:行号(垂直方向,从上到下递增)
    void gotoxy(int x, int y) {
    COORD coord; // 定义坐标结构体变量
    coord.X = x; // 设置列坐标
    coord.Y = y; // 设置行坐标
    // GetStdHandle(STD_OUTPUT_HANDLE) 获取标准输出设备句柄
    // SetConsoleCursorPosition 设置光标到指定坐标位置
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    }


    2.3 清屏函数 system("cls")

    知识点说明:

    system() 函数定义在 stdlib.h 中,用于执行操作系统命令。system("cls") 是Windows平台下的清屏命令。

    #include <stdlib.h>

    system("cls"); // Windows下清屏
    // system("clear"); // Linux/Mac下清屏


    2.4 入门案例:简单的开始界面

    #include <stdio.h> // 包含标准输入输出函数
    #include <stdlib.h> // 包含system函数
    #include <windows.h> // 包含Windows API函数

    // 自定义函数:将光标定位到(x, y)坐标位置
    void gotoxy(int x, int y) {
    COORD coord; // 定义坐标结构体
    coord.X = x; // 设置水平坐标
    coord.Y = y; // 设置垂直坐标
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    }

    int main() {
    system("cls"); // 清屏,确保界面干净

    // 在第10行第25列开始显示游戏标题
    gotoxy(25, 10);
    printf("================================");

    gotoxy(25, 11);
    printf("= =");

    gotoxy(25, 12);
    printf("= C语言单词背记闯关游戏 =");

    gotoxy(25, 13);
    printf("= =");

    gotoxy(25, 14);
    printf("================================");

    // 在下方显示提示信息
    gotoxy(30, 16);
    printf("按任意键开始游戏…");

    getchar(); // 等待用户按下回车键

    return 0; // 程序正常结束
    }


    三、积分规则界面显示

    3.1 字符数组与字符串

    知识点说明:

    C语言中没有专门的"字符串"类型,字符串用字符数组来存储。字符串以空字符 '\\0' 作为结束标志。

    字符串的两种初始化方式:

    // 方式1:逐字符初始化(注意手动加 '\\0')
    char str1[6] = {'H', 'e', 'l', 'l', 'o', '\\0'};

    // 方式2:用字符串常量初始化(编译器自动添加 '\\0')
    char str2[] = "Hello"; // 实际占用6个字节(5个字符 + 1个 '\\0')

    重要区别:

    • char str[] = "Hello"; — 数组内容可修改
    • char *str = "Hello"; — 指向字符串常量,内容不可修改

    字符串长度与数组大小的关系:

    char s[] = "abc";
    // sizeof(s) = 4 (数组实际大小:3个字符 + 1个 '\\0')
    // strlen(s) = 3 (字符串有效长度:不计算 '\\0')


    3.2 字符串长度函数 strlen

    函数原型:

    #include <string.h>
    size_t strlen(const char *str);

    • 功能:返回字符串中 '\\0' 之前的字符个数(不含 '\\0')
    • 参数:以 '\\0' 结尾的字符串
    • 返回值:字符串的有效长度(无符号整数)

    示例:

    #include <stdio.h>
    #include <string.h>

    int main() {
    char word[] = "apple"; // 定义字符串

    int len = strlen(word); // 计算字符串长度

    printf("单词: %s\\n", word); // 输出:单词: apple
    printf("长度: %d\\n", len); // 输出:长度: 5

    return 0;
    }


    3.3 格式化输出实现规则界面

    入门案例:积分规则显示界面

    #include <stdio.h> // 包含printf函数
    #include <stdlib.h> // 包含system函数
    #include <windows.h> // 包含Windows API

    // 光标定位函数
    void gotoxy(int x, int y) {
    COORD coord; // 定义坐标结构体
    coord.X = x; // 设置列坐标
    coord.Y = y; // 设置行坐标
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    }

    int main() {
    system("cls"); // 清屏

    // 标题
    gotoxy(30, 3);
    printf("*** 游戏规则 ***");

    // 水平分隔线
    gotoxy(20, 5);
    printf("—————————————-");

    // 规则内容(逐行显示)
    gotoxy(22, 7);
    printf("1. 游戏共有 5 关,每关 4 个单词");

    gotoxy(22, 9);
    printf("2. 每关给出一个英文单词");

    gotoxy(22, 11);
    printf("3. 从4个中文释义中选出正确答案");

    gotoxy(22, 13);
    printf("4. 每答对一题得 20 分");

    gotoxy(22, 15);
    printf("5. 每关满分 100 分");

    gotoxy(22, 17);
    printf("6. 答错不扣分,但不能获得该题分数");

    // 水平分隔线
    gotoxy(20, 19);
    printf("—————————————-");

    // 提示
    gotoxy(28, 21);
    printf("按任意键开始游戏…");

    getchar(); // 等待用户输入

    return 0; // 程序正常结束
    }


    四、单词背记游戏主功能实现

    4.1 二维字符数组(二维字符串数组)

    知识点说明:

    二维字符数组可以存储多个字符串,每一行存储一个字符串。在单词游戏中,通常用二维数组存储单词及其对应的中文释义。

    声明与初始化:

    // 声明一个3行20列的二维字符数组,可以存储3个长度不超过19的字符串
    char words[3][20] = {
    "apple", // words[0] = "apple"
    "banana", // words[1] = "banana"
    "cherry" // words[2] = "cherry"
    };

    内存布局示意:

    words[0]: 'a' 'p' 'p' 'l' 'e' '\\0' …(剩余空间)
    words[1]: 'b' 'a' 'n' 'a' 'n' 'a' '\\0' …
    words[2]: 'c' 'h' 'e' 'r' 'r' 'y' '\\0' …

    访问单个字符串:

    printf("%s\\n", words[0]); // 输出整个字符串:apple
    printf("%c\\n", words[0][2]); // 输出单个字符:p


    4.2 strcmp 字符串比较函数

    知识点说明:

    C语言中不能直接用 == 比较两个字符串的内容(== 比较的是地址),必须使用 strcmp 函数逐字符比较。

    函数原型:

    #include <string.h>
    int strcmp(const char *str1, const char *str2);

    返回值规则:

    返回值含义
    0 两个字符串完全相同
    > 0 str1 大于 str2(按ASCII码比较)
    < 0 str1 小于 str2(按ASCII码比较)

    比较规则:

    • 从第一个字符开始,逐个比较对应位置的字符的ASCII码值
    • 遇到不同的字符或 '\\0' 时停止比较
    • 如果所有字符都相同且同时遇到 '\\0',返回 0

    入门案例:

    #include <stdio.h>
    #include <string.h>

    int main() {
    char answer[20]; // 存储用户输入的答案
    char correct[20] = "apple"; // 存储正确答案

    printf("请输入 apple 的中文意思: ");
    scanf("%s", answer); // 读取用户输入

    // strcmp 返回 0 表示两个字符串相同
    if (strcmp(answer, correct) == 0) {
    printf("回答正确!\\n"); // 字符串匹配
    } else {
    printf("回答错误!正确答案是: %s\\n", correct); // 字符串不匹配
    }

    return 0;
    }


    4.3 strcpy 字符串复制函数

    知识点说明:

    strcpy 将源字符串复制到目标字符数组中(包括 '\\0')。

    函数原型:

    #include <string.h>
    char *strcpy(char *dest, const char *src);

    • dest:目标字符数组(必须足够大)
    • src:源字符串
    • 返回值:目标数组的首地址

    示例:

    #include <stdio.h>
    #include <string.h>

    int main() {
    char dest[20]; // 目标数组,需要足够大
    char src[] = "Hello"; // 源字符串

    strcpy(dest, src); // 将 src 复制到 dest
    printf("dest = %s\\n", dest); // 输出:dest = Hello

    return 0;
    }

    注意: 不能用赋值号 = 直接给字符数组赋值字符串(初始化时可以,赋值时不行):

    char s[20];
    // s = "Hello"; // 错误!数组名是常量地址,不能被赋值
    strcpy(s, "Hello"); // 正确!使用 strcpy 函数


    4.4 随机数函数 rand 和 srand

    知识点说明:

    在单词游戏中,需要随机选择题目,这就需要用到随机数。

    相关函数:

    #include <stdlib.h>
    int rand(void); // 返回一个 0 到 RAND_MAX 之间的伪随机整数
    void srand(unsigned int seed); // 设置随机数种子

    rand() 的特点:

    • 返回范围:0 ~ RAND_MAX(至少32767)
    • 每次调用返回一个伪随机数
    • 如果种子相同,生成的随机数序列完全相同

    srand() 的作用:

    • 设置随机数生成器的起始值(种子)
    • 种子不同 → 生成不同的随机数序列
    • 常用 time(0)(当前时间戳)作为种子,保证每次运行不同

    生成指定范围的随机数:

    // 生成 [min, max] 范围内的随机整数
    rand() % (max – min + 1) + min

    入门案例:

    #include <stdio.h>
    #include <stdlib.h> // 包含 rand() 和 srand()
    #include <time.h> // 包含 time()

    int main() {
    // 用当前时间作为随机数种子(只需调用一次)
    srand((unsigned int)time(NULL));

    // 生成10个 1~100 之间的随机数
    int i;
    for (i = 0; i < 10; i++) {
    int num = rand() % 100 + 1; // rand()%100 得到 0~99,+1 得到 1~100
    printf("%d ", num);
    }
    printf("\\n");

    // 生成 0~3 的随机数(用于从4个选项中随机选一个位置)
    int pos = rand() % 4; // 结果为 0, 1, 2, 3 中的一个
    printf("随机选项位置: %d\\n", pos);

    return 0;
    }


    4.5 switch-case 多分支选择语句

    知识点说明:

    switch 语句根据一个整型表达式的值,跳转到对应的 case 分支执行。适用于多分支且判断条件为离散值的场景。

    语法格式:

    switch (表达式) {
    case 常量1:
    语句块1;
    break; // 跳出switch
    case 常量2:
    语句块2;
    break;
    // … 可以有多个case
    default: // 所有case都不匹配时执行
    语句块n;
    break;
    }

    执行流程:

  • 计算 switch 后面表达式的值
  • 从上到下依次与每个 case 后面的常量比较
  • 找到匹配的 case,执行其后的语句
  • 遇到 break 则跳出 switch;没有 break 则继续向下执行(称为"穿透")
  • 如果没有匹配的 case,执行 default 分支(如果有的话)
  • 注意事项:

    • case 后面必须是常量表达式(整数或字符),不能是变量
    • break 不可遗漏,否则会发生穿透
    • default 可以省略

    入门案例:

    #include <stdio.h>

    int main() {
    int choice; // 存储用户选择

    printf("请选择答案(输入1-4的数字): ");
    scanf("%d", &choice);

    switch (choice) {
    case 1:
    printf("你选择了选项 A\\n");
    break;
    case 2:
    printf("你选择了选项 B\\n");
    break;
    case 3:
    printf("你选择了选项 C\\n");
    break;
    case 4:
    printf("你选择了选项 D\\n");
    break;
    default:
    printf("输入无效,请输入1-4之间的数字\\n");
    break;
    }

    return 0;
    }


    4.6 数组元素的随机排列(打乱顺序)

    知识点说明:

    为了让每关的选项顺序不同,需要将选项数组中的元素随机打乱。常用 Fisher-Yates 洗牌算法 的简化版本。

    算法思路:

    从数组最后一个元素开始,依次与前面某个随机位置的元素交换

    入门案例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <string.h>

    int main() {
    // 定义4个选项字符串
    char options[4][30] = {
    "苹果", // 选项0
    "香蕉", // 选项1
    "汽车", // 选项2
    "书本" // 选项3
    };

    srand((unsigned int)time(NULL)); // 初始化随机种子

    // Fisher-Yates 洗牌算法(简化版)
    // 从最后一个元素开始,随机选一个前面的元素交换
    int i;
    for (i = 3; i > 0; i—) {
    int j = rand() % (i + 1); // 生成 0~i 的随机索引

    // 交换 options[i] 和 options[j]
    char temp[30]; // 临时数组用于交换
    strcpy(temp, options[i]); // temp = options[i]
    strcpy(options[i], options[j]); // options[i] = options[j]
    strcpy(options[j], temp); // options[j] = temp
    }

    // 输出打乱后的选项
    printf("打乱后的选项顺序:\\n");
    for (i = 0; i < 4; i++) {
    printf(" %d. %s\\n", i + 1, options[i]);
    }

    return 0;
    }


    4.7 循环结构(for、while、do-while)

    知识点说明:

    循环结构用于重复执行一段代码。在单词游戏中,需要循环遍历每一关、每一道题目。

    4.7.1 for 循环

    语法:

    for (初始化表达式; 循环条件; 更新表达式) {
    循环体语句;
    }

    执行流程:

  • 执行初始化表达式(仅一次)
  • 判断循环条件:为真 → 执行循环体;为假 → 退出循环
  • 执行循环体
  • 执行更新表达式
  • 回到步骤2
  • 示例:

    // 遍历5关,每关4道题
    int round, question;
    for (round = 0; round < 5; round++) { // 外层循环:5关
    printf("第 %d 关\\n", round + 1);
    for (question = 0; question < 4; question++) { // 内层循环:4道题
    printf(" 题目 %d\\n", question + 1);
    }
    }

    4.7.2 while 循环

    语法:

    while (循环条件) {
    循环体语句;
    }

    特点: 先判断条件,再决定是否执行循环体(可能一次都不执行)。

    4.7.3 do-while 循环

    语法:

    do {
    循环体语句;
    } while (循环条件); // 注意:这里有分号!

    特点: 先执行一次循环体,再判断条件(至少执行一次)。

    入门案例(do-while实现输入验证):

    #include <stdio.h>

    int main() {
    int choice;

    do {
    printf("请输入1-4选择答案: ");
    scanf("%d", &choice);

    if (choice < 1 || choice > 4) {
    printf("输入无效,请重新输入!\\n");
    }
    } while (choice < 1 || choice > 4); // 条件为真则继续循环

    printf("你选择了: %d\\n", choice);

    return 0;
    }


    4.8 条件判断(if-else 嵌套与逻辑运算符)

    知识点说明:

    在游戏主功能中,需要大量使用条件判断来判断用户答案是否正确、是否过关等。

    逻辑运算符:

    运算符含义示例结果
    && 逻辑与(都为真才为真) (a > 0) && (a < 10) a在1-9范围内为真
    || 逻辑或(一个为真就为真) (a == 1) || (a == 2) a为1或2时为真
    ! 逻辑非(取反) !(a == 0) a不为0时为真

    入门案例:判断是否过关

    #include <stdio.h>

    int main() {
    int score = 80; // 当前得分
    int level = 1; // 当前关卡

    // 多条件判断
    if (score >= 60 && level < 5) {
    printf("恭喜!通过第 %d 关,得分: %d\\n", level, score);
    printf("进入下一关!\\n");
    } else if (score >= 60 && level == 5) {
    printf("恭喜!通关所有关卡!总得分: %d\\n", score);
    } else {
    printf("很遗憾,得分 %d 未达到60分,闯关失败!\\n", score);
    }

    return 0;
    }


    4.9 getch 函数(无回显读取按键)

    知识点说明:

    getch() 从键盘读取一个字符,但不会回显到屏幕上,也不需要按回车确认。适用于游戏中的按键操作(如按数字键选择答案)。

    #include <conio.h> // Windows平台特有头文件
    char ch = getch(); // 读取一个按键,不回显,无需回车

    与 getchar 的区别:

    特性getch()getchar()
    头文件 conio.h stdio.h
    回显 不回显 回显到屏幕
    缓冲 无缓冲,立即返回 有缓冲,按回车后返回
    平台 Windows特有 所有平台

    示例:

    #include <stdio.h>
    #include <conio.h>

    int main() {
    printf("请按任意键继续…");
    char ch = getch(); // 等待按键,不显示按下的键
    printf("\\n你按下了: %c\\n", ch); // 显示按下的字符

    // 利用getch实现数字选择
    printf("请选择(1-4): ");
    ch = getch(); // 直接读取数字键
    printf("%c\\n", ch); // 回显选择

    if (ch >= '1' && ch <= '4') {
    printf("你选择了选项 %c\\n", ch);
    } else {
    printf("无效选择\\n");
    }

    return 0;
    }


    4.10 综合案例:单词选择题核心逻辑

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <time.h>
    #include <conio.h>
    #include <windows.h>

    // 光标定位函数
    void gotoxy(int x, int y) {
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    }

    int main() {
    // 定义单词数据:英文单词 + 对应中文释义
    char english[4][20] = {
    "apple", // 单词0
    "banana", // 单词1
    "computer", // 单词2
    "teacher" // 单词3
    };

    // meanings[i] 是 english[i] 的正确中文释义
    char meanings[4][30] = {
    "苹果", // apple 的释义
    "香蕉", // banana 的释义
    "计算机", // computer 的释义
    "老师" // teacher 的释义
    };

    // 干扰释义(用于生成错误选项)
    char wrongMeanings[4][30] = {
    "汽车",
    "桌子",
    "天空",
    "河流"
    };

    srand((unsigned int)time(NULL)); // 初始化随机种子

    int score = 0; // 总得分
    int i, j;

    // 循环出题(遍历每个单词)
    for (i = 0; i < 4; i++) {
    system("cls"); // 清屏

    // 显示当前题目
    gotoxy(30, 3);
    printf("第 %d 题 (共 4 题)", i + 1);
    gotoxy(30, 5);
    printf("单词: %s", english[i]); // 显示英文单词
    gotoxy(30, 6);
    printf("请选择正确的中文释义:");

    // 准备4个选项:1个正确 + 3个错误
    char options[4][30]; // 4个选项
    int correctPos = rand() % 4; // 随机决定正确答案放在哪个位置

    // 将正确答案放入随机位置
    strcpy(options[correctPos], meanings[i]);

    // 填充其余3个错误选项
    int wrongIndex = 0; // 错误选项索引
    for (j = 0; j < 4; j++) {
    if (j != correctPos) {
    // 跳过正确答案的位置,填入错误选项
    while (strcmp(wrongMeanings[wrongIndex], meanings[i]) == 0) {
    wrongIndex++; // 避免错误选项与正确答案重复
    }
    strcpy(options[j], wrongMeanings[wrongIndex]);
    wrongIndex++;
    }
    }

    // 显示4个选项
    for (j = 0; j < 4; j++) {
    gotoxy(32, 8 + j * 2);
    printf("%d. %s", j + 1, options[j]);
    }

    // 获取用户选择
    gotoxy(30, 17);
    printf("你的选择(1-4): ");
    char ch = getch(); // 无回显读取按键
    printf("%c\\n", ch); // 回显选择

    int userChoice = ch – '0'; // 将字符'1'-'4'转换为整数1-4

    // 判断答案是否正确
    if (userChoice – 1 == correctPos) {
    // 用户选择的索引 == 正确答案的索引
    gotoxy(30, 19);
    printf("回答正确!+20分");
    score += 20; // 加分
    } else {
    gotoxy(30, 19);
    printf("回答错误!正确答案是: %s", meanings[i]);
    }

    gotoxy(30, 21);
    printf("当前得分: %d", score);

    gotoxy(30, 23);
    printf("按任意键继续…");
    getch(); // 等待用户按键后继续下一题
    }

    // 显示最终结果
    system("cls");
    gotoxy(30, 10);
    printf("游戏结束!最终得分: %d / 80", score);

    getchar(); // 等待回车
    return 0;
    }


    五、读取和存储游戏最高分

    5.1 文件指针 FILE *

    知识点说明:

    C语言中所有文件操作都通过文件指针进行。FILE 是标准库定义的结构体类型,包含了文件操作所需的各种信息(文件位置、缓冲区、状态等)。

    #include <stdio.h>
    FILE *fp; // 声明一个 FILE 类型的指针变量

    文件指针的作用:

    • 指向一个打开的文件
    • 所有后续的读写操作都通过该指针进行
    • 文件操作完成后必须关闭

    5.2 文件打开函数 fopen

    函数原型:

    FILE *fopen(const char *filename, const char *mode);

    • filename:文件路径(字符串)
    • mode:打开模式
    • 返回值:成功返回文件指针,失败返回 NULL

    常用打开模式:

    模式含义文件不存在时文件存在时
    "r" 只读 打开失败 从头读取
    "w" 只写 创建新文件 清空文件内容
    "a" 追加写 创建新文件 在末尾追加
    "r+" 读写 打开失败 从头读写
    "w+" 读写 创建新文件 清空后读写
    "a+" 读和追加 创建新文件 可读,写在末尾

    5.3 文件关闭函数 fclose

    函数原型:

    int fclose(FILE *fp);

    • 关闭文件,释放缓冲区资源
    • 成功返回 0,失败返回 EOF
    • 每次打开文件后必须关闭

    5.4 格式化文件读写 fprintf 和 fscanf

    知识点说明:

    fprintf 和 fscanf 的用法与 printf 和 scanf 基本相同,只是多了一个文件指针参数,操作对象从屏幕/键盘变成了文件。

    fprintf — 向文件写入格式化数据:

    int fprintf(FILE *fp, const char *format, ...);
    // 成功返回写入的字符数,失败返回负数

    fscanf — 从文件读取格式化数据:

    int fscanf(FILE *fp, const char *format, ...);
    // 返回成功读取的数据项个数

    入门案例:保存和读取最高分

    #include <stdio.h>

    // 函数功能:将最高分保存到文件中
    // 参数 score:要保存的分数值
    void saveHighScore(int score) {
    FILE *fp; // 声明文件指针
    fp = fopen("highscore.txt", "w"); // 以写模式打开文件

    if (fp == NULL) { // 检查文件是否成功打开
    printf("错误:无法打开文件进行写入!\\n");
    return; // 打开失败则返回
    }

    fprintf(fp, "%d", score); // 将分数以整数格式写入文件
    fclose(fp); // 关闭文件

    printf("最高分 %d 已保存到文件\\n", score);
    }

    // 函数功能:从文件中读取最高分
    // 返回值:读取到的最高分,如果文件不存在则返回0
    int loadHighScore() {
    FILE *fp; // 声明文件指针
    int highScore = 0; // 默认最高分为0

    fp = fopen("highscore.txt", "r"); // 以读模式打开文件

    if (fp == NULL) { // 文件不存在(首次运行时)
    return 0; // 返回默认值0
    }

    fscanf(fp, "%d", &highScore); // 从文件读取整数到 highScore
    fclose(fp); // 关闭文件

    return highScore; // 返回读取到的最高分
    }

    int main() {
    // 读取历史最高分
    int highest = loadHighScore();
    printf("历史最高分: %d\\n", highest);

    int currentScore = 85; // 假设当前游戏得分为85
    printf("当前得分: %d\\n", currentScore);

    // 如果当前分数超过历史最高分,则更新
    if (currentScore > highest) {
    printf("新纪录!保存新的最高分…\\n");
    saveHighScore(currentScore); // 保存新记录
    } else {
    printf("未超过历史最高分\\n");
    }

    // 再次读取验证
    printf("现在最高分: %d\\n", loadHighScore());

    return 0;
    }


    5.5 文件操作的错误处理

    知识点说明:

    文件操作可能因为各种原因失败(磁盘满、权限不足、路径错误等),因此每次文件操作后都应该检查返回值。

    FILE *fp = fopen("data.txt", "r");

    // 必须检查 fopen 返回值
    if (fp == NULL) {
    printf("文件打开失败!\\n");
    // 可以选择退出程序
    exit(1); // exit(0) 正常退出,exit(非0) 异常退出
    }

    // … 进行文件操作 …

    fclose(fp); // 操作完成后关闭文件


    5.6 综合案例:完整的最高分管理模块

    #include <stdio.h>
    #include <stdlib.h>

    #define SCORE_FILE "gamescore.dat" // 定义存储最高分的文件名

    // 结构体:存储游戏记录
    typedef struct {
    int highScore; // 历史最高分
    char playerName[20]; // 最高分保持者姓名
    int playCount; // 游戏总次数
    } GameRecord;

    // 函数功能:从文件加载游戏记录
    // 参数 record:指向 GameRecord 结构体的指针
    void loadRecord(GameRecord *record) {
    FILE *fp = fopen(SCORE_FILE, "r"); // 尝试打开文件

    if (fp == NULL) {
    // 文件不存在,初始化默认值
    record->highScore = 0; // 最高分为0
    strcpy(record->playerName, "无"); // 无记录保持者
    record->playCount = 0; // 游戏次数为0
    return;
    }

    // 从文件读取记录的三个字段
    fscanf(fp, "%d %s %d",
    &(record->highScore), // 读取最高分
    record->playerName, // 读取玩家姓名
    &(record->playCount)); // 读取游戏次数

    fclose(fp); // 关闭文件
    }

    // 函数功能:将游戏记录保存到文件
    // 参数 record:指向 GameRecord 结构体的指针
    void saveRecord(GameRecord *record) {
    FILE *fp = fopen(SCORE_FILE, "w"); // 以写模式打开文件

    if (fp == NULL) {
    printf("保存失败:无法写入文件!\\n");
    return;
    }

    // 将记录的三个字段写入文件
    fprintf(fp, "%d %s %d",
    record->highScore, // 写入最高分
    record->playerName, // 写入玩家姓名
    record->playCount); // 写入游戏次数

    fclose(fp); // 关闭文件
    }

    int main() {
    GameRecord record; // 定义游戏记录结构体变量

    loadRecord(&record); // 加载历史记录

    printf("历史最高分: %d (保持者: %s)\\n",
    record.highScore, record.playerName);
    printf("历史游戏次数: %d\\n", record.playCount);

    // 模拟新一局游戏
    int newScore = 90; // 模拟新得分
    record.playCount++; // 游戏次数加1

    if (newScore > record.highScore) {
    printf("新纪录!请输入你的名字: ");
    scanf("%s", record.playerName); // 记录新纪录保持者
    record.highScore = newScore; // 更新最高分
    saveRecord(&record); // 保存到文件
    printf("记录已保存!\\n");
    }

    return 0;
    }


    六、游戏结束界面显示

    6.1 综合运用光标定位进行界面布局

    知识点说明:

    游戏结束界面需要显示:最终得分、是否刷新纪录、历史最高分、是否重新游戏等信息。通过 gotoxy 精确定位各元素位置,配合 printf 输出,实现美观的界面布局。


    6.2 sprintf 格式化到字符串

    知识点说明:

    sprintf 的用法与 printf 相同,但输出目标不是屏幕,而是一个字符数组(字符串)。常用于将数字等数据格式化为字符串以便进一步处理。

    #include <stdio.h>
    int sprintf(char *str, const char *format, ...);

    示例:

    char buffer[50]; // 存储格式化结果的字符数组
    int score = 85;
    sprintf(buffer, "最终得分: %d 分", score); // 将格式化内容写入buffer
    printf("%s\\n", buffer); // 输出:最终得分: 85 分


    6.3 getchar 字符输入函数

    知识点说明:

    getchar() 从标准输入读取一个字符(包括回车符),有缓冲机制,需要按回车确认。

    #include <stdio.h>
    int getchar(void); // 返回读取到的字符(int类型),失败返回EOF

    常见用法:

    // 用法1:等待用户按回车
    printf("按回车键继续…");
    getchar(); // 等待回车

    // 用法2:读取字符
    char ch = getchar(); // 读取一个字符

    注意: scanf 读取数据后缓冲区可能残留换行符 '\\n',后续 getchar() 会直接读到这个换行符。解决方法:

    scanf("%d", &num);
    getchar(); // 吃掉缓冲区中残留的换行符
    ch = getchar(); // 现在才能正确等待用户输入


    6.4 综合案例:游戏结束界面完整实现

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <conio.h>
    #include <windows.h>

    // 光标定位函数
    void gotoxy(int x, int y) {
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    }

    // 函数功能:显示游戏结束界面
    // 参数 score:本局得分
    // 参数 highScore:历史最高分
    // 返回值:用户选择 1=重新开始,0=退出游戏
    int showGameOver(int score, int highScore) {
    system("cls"); // 清屏

    // ======== 绘制边框 ========
    gotoxy(20, 4);
    printf("╔══════════════════════════════════════╗"); // 顶部边框

    int i;
    for (i = 5; i <= 18; i++) { // 左右边框
    gotoxy(20, i);
    printf("║ ║");
    }

    gotoxy(20, 19);
    printf("╚══════════════════════════════════════╝"); // 底部边框

    // ======== 标题 ========
    gotoxy(33, 6);
    printf("游 戏 结 束");

    // ======== 水平分隔线 ========
    gotoxy(23, 8);
    printf("──────────────────────────────────");

    // ======== 显示得分 ========
    gotoxy(28, 10);
    printf("本局得分: %d 分", score);

    // ======== 判断是否刷新纪录 ========
    gotoxy(28, 12);
    if (score > highScore) {
    printf("★ 恭喜!刷新历史最高分!★"); // 新纪录
    gotoxy(28, 13);
    printf(" 历史最高分: %d → %d", highScore, score);
    } else {
    printf("历史最高分: %d 分", highScore); // 未刷新
    gotoxy(28, 13);
    printf("还需努力哦!");
    }

    // ======== 水平分隔线 ========
    gotoxy(23, 15);
    printf("──────────────────────────────────");

    // ======== 操作选项 ========
    gotoxy(25, 17);
    printf("1. 再来一局 0. 退出游戏");

    // ======== 获取用户选择 ========
    gotoxy(28, 21);
    printf("请输入选择: ");

    char ch = getch(); // 无回显读取按键
    printf("%c", ch); // 回显选择

    if (ch == '1') {
    return 1; // 返回1表示重新开始
    } else {
    return 0; // 返回0表示退出
    }
    }

    int main() {
    int currentScore = 75; // 模拟当前得分
    int highestScore = 60; // 模拟历史最高分

    // 调用结束界面函数
    int choice = showGameOver(currentScore, highestScore);

    if (choice == 1) {
    printf("\\n即将开始新一局…\\n");
    // 这里可以调用游戏重新开始的逻辑
    } else {
    printf("\\n感谢游玩,再见!\\n");
    }

    return 0;
    }


    七、补充知识点

    7.1 typedef 类型别名

    知识点说明:

    typedef 可以为已有的数据类型定义一个新的名字,提高代码可读性。

    typedef 原类型名 新类型名;

    示例:

    typedef int Score; // Score 等价于 int
    typedef char String20[20]; // String20 等价于 char[20]

    Score s = 100; // 等价于 int s = 100;
    String20 name = "hello"; // 等价于 char name[20] = "hello";


    7.2 结构体 struct 基础

    知识点说明:

    结构体允许将不同类型的数据组合在一起,形成一个新的复合数据类型。

    定义与使用:

    // 定义结构体类型
    struct Word {
    char english[30]; // 英文单词
    char chinese[30]; // 中文释义
    int difficulty; // 难度等级
    };

    // 声明结构体变量并初始化
    struct Word w1 = {"apple", "苹果", 1};

    // 访问结构体成员用 "."
    printf("单词: %s\\n", w1.english);
    printf("释义: %s\\n", w1.chinese);
    printf("难度: %d\\n", w1.difficulty);

    用 typedef 简化结构体:

    typedef struct {
    char english[30];
    char chinese[30];
    int difficulty;
    } Word; // Word 现在是类型名,不需要 struct 关键字

    Word w1 = {"apple", "苹果", 1}; // 声明更简洁


    7.3 函数的定义与调用

    知识点说明:

    将重复使用的功能封装成函数,可以提高代码复用性和可维护性。

    函数定义的一般形式:

    返回类型 函数名(参数类型 参数名, ...) {
    函数体;
    return 返回值; // void类型函数可省略return
    }

    入门案例:封装得分计算函数

    #include <stdio.h>

    // 函数功能:判断答案是否正确并计算得分
    // 参数 correctAnswer:正确答案的索引(0~3)
    // 参数 userAnswer:用户选择的索引(0~3)
    // 参数 currentScore:当前累计得分
    // 返回值:更新后的总得分
    int checkAnswer(int correctAnswer, int userAnswer, int currentScore) {
    if (correctAnswer == userAnswer) { // 判断是否答对
    printf("回答正确!+20分\\n");
    return currentScore + 20; // 答对加20分,返回新分数
    } else {
    printf("回答错误!正确答案是第 %d 个选项\\n", correctAnswer + 1);
    return currentScore; // 答错不加分,返回原分数
    }
    }

    // 函数功能:判断是否通关
    // 参数 score:当前总得分
    // 参数 totalRounds:总关数
    // 返回值:1表示通关,0表示未通关
    int isPass(int score, int totalRounds) {
    int passScore = totalRounds * 60; // 通关最低分 = 关数 × 60
    if (score >= passScore) {
    return 1; // 达到通关分数
    }
    return 0; // 未达到
    }

    int main() {
    int score = 0; // 初始化总分为0

    // 模拟3道题的回答
    score = checkAnswer(2, 2, score); // 正确答案是2,用户选了2(对)
    score = checkAnswer(0, 3, score); // 正确答案是0,用户选了3(错)
    score = checkAnswer(1, 1, score); // 正确答案是1,用户选了1(对)

    printf("\\n最终得分: %d\\n", score); // 输出:最终得分: 40

    // 判断是否通关
    if (isPass(score, 3)) {
    printf("恭喜通关!\\n");
    } else {
    printf("未通关,继续努力!\\n");
    }

    return 0;
    }


    7.4 程序暂停函数 Sleep

    知识点说明:

    Sleep() 函数(Windows平台)让程序暂停指定的毫秒数,常用于游戏中的延迟效果。

    #include <windows.h>
    Sleep(1000); // 暂停1000毫秒(即1秒)

    • 参数单位是毫秒(1秒 = 1000毫秒)
    • 注意 S 大写
    • Linux平台下对应函数为 sleep(秒数)

    示例(倒计时效果):

    #include <stdio.h>
    #include <windows.h>

    int main() {
    int i;
    for (i = 3; i >= 1; i—) {
    printf("\\r%d…", i); // \\r 回到行首,覆盖输出
    fflush(stdout); // 刷新输出缓冲区
    Sleep(1000); // 暂停1秒
    }
    printf("\\r开始! \\n"); // 覆盖之前的倒计时文字

    return 0;
    }


    7.5 ASCII码与字符转换

    知识点说明:

    在C语言中,字符在内存中以ASCII码值存储。字符 '0' 到 '9' 的ASCII码是连续的(48~57),可以利用这一特性进行字符与数字的转换。

    // 字符转数字
    char ch = '5';
    int num = ch – '0'; // num = 5('5'的ASCII码53 – '0'的ASCII码48 = 5)

    // 数字转字符
    int n = 7;
    char c = n + '0'; // c = '7'(7 + 48 = 55,即字符'7'的ASCII码)

    常用ASCII码值:

    字符ASCII码
    '0' ~ '9' 48 ~ 57
    'A' ~ 'Z' 65 ~ 90
    'a' ~ 'z' 97 ~ 122
    ' '(空格) 32
    '\\0'(空字符) 0

    八、知识点总览

    序号知识点所属模块关键语法/函数
    1 文件包含与宏定义 预处理模块 #include, #define, #ifndef
    2 格式化输出 游戏开始界面 printf, 转义字符, 格式说明符
    3 光标定位 开始/结束界面 gotoxy, COORD, SetConsoleCursorPosition
    4 二维字符数组 主功能 char arr[行][列]
    5 字符串操作 主功能 strcmp, strcpy, strlen
    6 随机数 主功能 rand, srand, time
    7 多分支选择 主功能 switch-case-break
    8 循环结构 主功能 for, while, do-while
    9 条件判断与逻辑运算 主功能 if-else, &&, ||, !
    10 按键输入 主功能/结束界面 getch, getchar
    11 文件指针与文件操作 最高分存储 FILE *, fopen, fclose
    12 格式化文件读写 最高分存储 fprintf, fscanf
    13 清屏与系统命令 各界面模块 system("cls")
    14 结构体 补充 struct, typedef
    15 函数定义与调用 补充 返回类型、参数、return
    16 程序暂停 补充 Sleep
    17 ASCII码转换 补充 字符与整数互转
    赞(0)
    未经允许不得转载:171主机测评 » C学习教程,从入门到精通,C语言单词背记闯关游戏 — 核心知识点详解(17)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址