资讯动态

C语言100例高效刷题路线:指针、链表与内存调试实战

发布时间:2026/9/19 19:55:26 来源:尧图企业网站定制
简介这是一份包含一百道C语言经典例题的PDF文档主要面向C语言初学者、高校计算机专业学生以及备考计算机二级C语言的读者。题目来源经典且覆盖全面包括基本数据类型、控制结构、函数、数组、指针、结构体、文件操作等核心知识模块每道题均配有题目描述、程序分析和完整源代码便于读者对照练习。例如程序1用三重循环与条件判断解决无重复三位数排列问题程序2利用数轴分界处理利润提成分段计算程序3使用sqrt函数判断完全平方数程序4通过switch和闰年判断计算某天是第几天。这些实例由浅入深既能帮助初学者理解语法要点也能训练编程逻辑与调试能力。文档为单个PDF文件压缩包大小仅2.77MB轻量便携已由1201人学习下载。整体而言这是一套适合系统练习与考前冲刺的C语言经典题库读者可边看边敲代码快速提升实际编程水平。1. 从第30题就放弃的C语言例题册子你的硬盘里大概率躺着不止一份《C语言经典例题100例.pdf》。大多数人拿到它的路径是相仿的前20题写得飞快30到50题开始翻答案六七十题之后直接看代码最后得出“这册子太老、题量注水”的结论然后合上文件等着下一次从第1题重新开始。真正的问题不在于题目数量而在于你把例题当成了“任务”而不是“标本”。这篇文章按我带人时习惯的做法把100例按知识点、难度和代码量重新切成五类给你一套今天就能动手的刷题路线顺带把指针、结构体、链表、文件读写这几类高频考点的坑摊开讲清楚。新手可以跟着章节一步步敲有经验的人可以重点看第2章的考点地图和第5章的测试框架。2. 100道例题的考点地图指针、内存与结构体的出现频率2.1 先按知识点把100例拆成五类拿到这份PDF的第一晚不要急着写代码拿一支笔把题目按知识点归类。常见做法是分成下面五类这种分法也对应着绝大多数C语言教材的章节顺序知识点常见题量占比典型题型上手难度基础语法与控制流25%素数判断、水仙花数、九九乘法表低数组与字符串25%冒泡排序、字符统计、字符串逆序低中指针与内存动态分配20%交换函数、二级指针、malloc/free中高结构体与链表20%学生成绩表、单链表反转、约瑟夫环中高文件读写与预处理10%文件拷贝、词频统计、带参宏中实际题量分布会因版本略有差异但大体就是这个比例。文件类题目占比最小原因是早期教材把文件放在最后一章篇幅也最长一本习题册放不下太多。归类做完你会立刻发现“字符串逆序”和“链表反转”名字不同考点高度重合都在考指针移动和边界判断。2.2 指针题最常见的三种出场方式指针被誉为C语言的门槛在100例里几乎无处不在。最简单的出场方式是函数传参写一个swap函数交换两个整数。很多新手第一版是这样写的void swap(int a, int b) { int temp a; a b; b temp; }这段代码语法完全正确但调用后实参没有变化。原因是C语言的函数参数默认是值传递a和b只是实参的拷贝。改成指针版本才能操作函数外面的变量void swap(int *a, int *b) { int temp *a; // 取出a指向地址里的值 *a *b; // 把b的值写入a指向的地址 *b temp; }调用时要写swap(x, y)。参数int *a表示接收一个int变量的地址函数内部通过解引用操作拿到或修改原变量。这个点能卡住很多人它也是理解数组名和字符串函数的基础。比如strcpy(dst, src)为什么能修改dst的内容就是因为它的内部拿着指针操作的是地址上的内存。2.3 链表题为什么总是“会看不会写”链表是另一个分水岭。结构体本身不难难的是节点之间的指针指向。常见例题是单链表反转struct Node { int data; struct Node *next; }; struct Node *reverseList(struct Node *head) { struct Node *prev NULL; while (head ! NULL) { struct Node *next head-next; // 先记下后一个节点 head-next prev; // 当前节点指向前驱 prev head; // prev整体后移 head next; // head整体后移 } return prev; }关键在局部指针next它必须在修改head-next之前保存下个节点的地址。很多人在白板上写链表题会卡在这一步因为先改了head-next导致原来的下一个节点再也找不回来。链表这类题动笔之前先想三件事谁指向谁、改变指向后会不会丢地址、循环退出时指针停在哪。配合画内存图可以少踩一半的坑。2.4 刷题顺序建议我建议按表格从上往下刷基础语法类和数组类先各做20道这些题让你熟练for、while、数组下标和基本输入输出。指针类和结构体类不要跳着做前面字符串函数没吃透就碰链表会非常吃力。文件类题放到最后因为它通常要把前面的知识点串起来。这样按层推进100例就不是一百个孤立题目而是一张由简到繁的知识网。3. 从字符串逆序拆开100例里的经典套路审题到验证3.1 一道题目里的边界条件“输入一个字符串将其逆序输出”在100例里出现频率高同时能考数组、指针、字符串函数和边界处理。很多人的第一版是这样写的#include stdio.h #include string.h int main() { char s[100]; gets(s); // 不推荐老题里却很常见 int len strlen(s); for (int i len - 1; i 0; i--) { printf(%c, s[i]); } return 0; }问题不在输出顺序而在gets函数它从标准输入读取字符串时不检查缓冲区长度如果输入超过99个字符就会越界写入s之后的相邻内存。现代C标准已经移除了gets更安全的是fgets(s, sizeof(s), stdin)。fgets的第二个参数是缓冲区大小第三个参数指定读取哪个流它会把末尾的换行符也读进来所以输出时要处理掉否则逆序结果会变成以换行符开头。3.2 三种典型实现方式对比第一种是原地交换也是面试笔试里比较喜欢的写法void reverseStr(char *s) { int left 0; int right strlen(s) - 1; while (left right) { char t s[left]; // 临时变量保存左侧字符 s[left] s[right]; // 右侧字符覆盖到左侧 s[right] t; // 保存的左侧字符写入右侧 left; right--; } }这种写法本质上是第2章swap指针思路的复用left和right是两个下标循环条件left right在奇偶长度下都成立。如果不写left或right--指针就永远停在原地死循环。第二种是用递归实现反转代码量小但调用栈深不适合长字符串适合用来理解函数调用过程void reverseRecursive(char *s, int low, int high) { if (low high) return; char t s[low]; s[low] s[high]; s[high] t; reverseRecursive(s, low 1, high - 1); }第三种是只打印不修改原字符串从len - 1往前输出。做题时选择哪一种取决于后续是否需要继续使用原字符串。下面这张表可以做快速选型实现方式时间复杂度额外空间是否修改原字符串原地交换O(n)O(1)是递归O(n)O(n)调用栈是逆序输出O(n)O(1)否3.3 用fscanf和fprintf把结果写进文件例题里写到文件时最常用的是fscanf和fprintf。要求是从文本文件逐行读入字符串每行逆序后写入另一个文件。#include stdio.h #include string.h int main() { char line[256]; FILE *fin fopen(input.txt, r); FILE *fout fopen(output.txt, w); if (fin NULL || fout NULL) { perror(fopen failed); return 1; } while (fgets(line, sizeof(line), fin) ! NULL) { size_t len strlen(line); if (len 0 line[len - 1] \n) { line[len - 1] \0; len--; } for (size_t i len; i 0; i--) { fputc(line[i - 1], fout); } fputc(\n, fout); } fclose(fin); fclose(fout); return 0; }fin和fout是两个FILE *类型的文件指针。fopen(input.txt, r)里“r”表示只读“w”表示只写output.txt不存在时创建存在时清空后重写。fgets(line, sizeof(line), fin)每次最多读256字节的一行代码里检查line[len - 1] \n是为了处理源文件末尾换行符的问题。这里很容易踩的坑是漏掉对换行的处理导致output.txt里每一行逆序后都多出一个空行。4. 100例里高频易错点与调试为什么你的代码“感觉对了但输出不对”4.1 浮点数相等判断C语言例题里有一类历久弥新的题判断浮点运算结果是否等于某个数。比如判断0.1 0.2是否等于0.3。直接写成if (a b)绝大多数情况下不成立因为浮点数在二进制里无法精确表示所有十进制小数。常见做法是使用误差范围#include math.h double a 0.1 0.2; double b 0.3; if (fabs(a - b) 1e-9) { printf(equal\n); } else { printf(not equal\n); }fabs计算绝对差值1e-9是误差阈值。阈值要按题目精度要求设置要求小数点后6位就取1e-7到1e-8。在Linux上用gcc编译时记得加-lm链接数学库完整命令是gcc test.c -lm -o test否则会报出对fabs的未定义引用。4.2 字符串函数与数组越界例题里让用strcpy复制字符串的位置很多很多人会忽略目标数组的长度。strcpy循环拷贝src字符到dst直到遇到\0它完全不关心dst实际有多大。如果src是长度11的“hello world”字符串目标数组却只有char dst[10]就会把数据写到dst边界之外。更稳妥的写法是用strncpy再加手动补结束符char src[] hello; char dst[4]; strncpy(dst, src, sizeof(dst) - 1); dst[sizeof(dst) - 1] \0;这里sizeof(dst) - 1保证最多复制3个字符留一个位置放\0。strncpy自己的坑在于如果src长度恰好等于n它不会自动补\0。所以多写一行dst[sizeof(dst) - 1] \0;看起来冗余却可以避免后续调用strlen时读越界。这类题考的核心从来不是函数原型背诵而是你有没有意识到“字符串本质是字符数组数组大小必须由调用者负责”。4.3 用valgrind和gdb精准定位问题例题代码写多了最气人的是程序不崩溃、输出却乱七八糟。这时不要盯着代码空想先把工具用起来。Linux下先检查内存问题gcc -g -o demo demo.c valgrind --leak-checkfull --show-leak-kindsall ./demo-g让编译产物带上调试符号表valgrind才能报告具体行号。--leak-checkfull输出每块泄漏内存的分配位置。输出里最值得关注的两类提示是“Invalid read/write”和“definitely lost”。前者表示数组下标越界或指针访问了非法地址后者表示malloc的内存没有free。C语言内存管理的绝大多数运行期问题用这一条命令就能暴露出来。如果只是逻辑不对用gdb在可疑行打断点gdb ./demo b 23 run print leftb 23给第23行下断点run运行程序print left查看当前变量值。下一步用next逐行执行观察left和right的变化。例题里80%的“反直觉输出”都来自变量在某个作用域被意外修改gdb把变量打印一遍通常比阅读十分钟代码更有效。对应这几类高频问题可以列一个自查表现象常见原因第一步排查手段输出乱码数组越界或字符串缺\0valgrind检查非法写结果差很小浮点数相等误判改用fabs差值比较程序崩溃指针未初始化或越界gdb查看调用栈文件内容缺失fclose前忘记刷新检查返回值5. 把100例变成你的第一份可维护代码5.1 给例题加测试用例不要再用眼睛验证刷到后期手动输入数据验证会越来越低效。我建议用assert做自动化断言把每个例题的边界场景固定下来#include assert.h #include string.h void reverseStr(char *s); // 你要测试的函数 void test_reverseStr(void) { char s[] abc; reverseStr(s); assert(strcmp(s, cba) 0); char s2[] ; reverseStr(s2); assert(strcmp(s2, ) 0); } int main(void) { test_reverseStr(); return 0; }assert中的表达式为假时程序会终止并打印出错行号。每当你修改了reverseStr实现重跑一次测试就能马上知道哪个边界场景被破坏了。链表反转、文件读写这类题目也能用同样的方式构造固定输入断言输出结果。这样练下去你不但完成了一道题还顺手攒下了一套可以回归测试的小型测试集。5.2 一个自查技巧先画内存图再写代码我在写任何涉及指针或数组的例题前都会先在草稿纸上画出“变量名、地址、地址里的值”三列。以单链表反转为例画三个节点然后一步步推演prev、head、next三个指针在每个循环周期的位置等推演出的循环能从头顺到尾再回到编辑器里写代码。这套习惯在C语言项目里比在Python或Java里更有价值因为它能逼你确认每个赋值操作发生在哪块内存上而不是停留在“感觉应该这样写”的层面。当你能独立写完绝大部分例题并且给它们配上断言测试之后不妨再往前一步把每道题的主逻辑抽成参数明确的函数不要把所有代码堆在main里。一百道题整理完你手里就有了一小撮命名清晰、边界严谨的C语言工具函数。以后在项目里遇到数组处理、字符串操作或链表遍历可以直接把你刷过的这套代码拿过来改改参数复用。例题的价值从来不是做一遍就够而是经历一次从题目到可复用函数的完整转化。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价