资讯动态

C语言刷题实战:翁恺习题集解析与评测避坑指南

发布时间:2026/10/14 11:06:45 来源:尧图企业网站定制
简介这是中国大学慕课翁恺教授《C语言程序设计》课程配套的习题集源码包面向正在学习C语言或准备期末上机考试的在校生与自学者。压缩包内共三十二个文件包含三十一个按章节命名的C语言源程序文件和一个说明文档覆盖基本数据类型、运算符、流程控制、函数、数组、指针、结构体、文件操作等核心知识点。每个题目对应一个独立文件命名包含题号与中文描述便于按需查阅。整个资源包仅十二千字节非常轻量可快速下载。目前已有五千八百二十二人学习下载。通过研读和运行这些示例程序学习者既能验证课堂所学也能模仿代码风格完成课后练习还可以按题号顺序系统复习或针对薄弱章节定向突破尤其适合查漏补缺和考前冲刺是一份高性价比的实践材料。1. C语言入门绕不开的翁恺习题集一份能少走半年弯路的刷题资源刚开始学C语言的人最容易踩的坑不是语法没看懂而是书看了、课听了一打开编辑器却不知道从哪行写起。某高校软件工程专业的A同学大一第一学期就被C语言卡了两个多月指针还没摸明白期末考试差点亮红灯。后来他照着翁恺老师在MOOC上的《C语言程序设计》配套习题集按顺序一道题一道题啃下来期末上机直接拿了优秀。这套名为zju_cmooc的习题集本质上是翁恺老师在中国大学MOOC平台课程配套的编程练习全收录覆盖变量、分支、循环、数组、函数、指针、结构体、文件操作等完整知识点。它不是那种只有答案的题库而是每道题都有明确输入输出格式、边界条件可验证的练习资源。对新手来说这是检验自己到底会不会写代码的试金石对正在准备复试或考二级C语言的从业者来说这是一套性价比极高的自测清单。这篇笔记会把习题集的整体结构、各模块最佳刷题顺序、常见错误和避坑方法拆开讲清楚。2. 从顺序结构到分支循环先把代码的“骨架”搭稳2.1 顺序结构作业格式化输出与变量运算的隐藏考点习题集的前几题看起来简单比如输入半径算圆面积、交换两个变量的值这类题。新手很容易觉得“这有什么好练的”直接跳到循环和数组。实际上顺序结构这几道题藏着一个很重要但容易被忽略的能力要求输出格式必须和题目完全一致。很多题库平台用特判脚本比对输出结果多一个空格、少一个换行都是“答案错误”。我当时刷这部分时总结出一套固定写法。以“输入两个整数交换后输出”为例常见做法是引入临时变量#include stdio.h int main() { int a, b, temp; // 输入两个整数 scanf(%d %d, a, b); // 交换核心必须用第三个变量暂存 temp a; a b; b temp; printf(a%d, b%d\n, a, b); return 0; }逻辑说明这段代码通过temp变量暂存a的值再把b赋给a最后把temp里的原a值赋给b完成交换。参数说明scanf格式串里的空格用于跳过输入中的空白字符printf里的逗号和等号是普通字符必须严格按题目要求输出否则评测会判错。对于顺序结构我一般建议额外注意浮点数精度处理。比如计算圆面积时圆周率用3.14还是用3.14159结果在小数点后两位会有差异。习题集这类题通常会在题目描述里指定精度所以每次做题先看输出样例确定用%f还是%.2lf。2.2 分支结构专项if-else层叠和switch使用边界分支结构在习题集中占有不小比重。题目表面上是“判断成绩等级”“判断闰年”这类经典场景但实际考察的是两个容易被扣分的点边界值处理和else if的层叠顺序。以“判断成绩等级”为例90分以上是A80到89是B以此类推。新手最容易写出的问题是顺序颠倒比如先判断if(grade 60)输出D后面再判断if(grade 90)输出A——这种情况下90分以上永远走不到A分支。规范的写法是从高到低或从低到高单向推进#include stdio.h int main() { int score; scanf(%d, score); // 从高分段往下判避免区间重叠 if (score 90) { printf(A\n); } else if (score 80) { printf(B\n); } else if (score 70) { printf(C\n); } else if (score 60) { printf(D\n); } else { printf(F\n); } return 0; }逻辑说明每个else if前的条件隐式包含了“不满足上一个条件”这一前提因此score 80实际上表示80到89之间。参数说明这种写法省去range判断的与运算同时避免条件覆盖漏洞。有些题目用switch更合适比如“输入数字输出对应星期”这种单变量多分支场景但要注意switch在C语言里不会自动跳出分支每个case后面必须写break。习题集里会有专门针对这个坑的题目。2.3 循环结构for与while的选择依据和死循环排查循环是多数新手第一次真正“卡壳”的地方。习题集的循环题量很大包括求和、统计位数、数字逆序、最大公约数等。刷这部分我的经验是知道什么时候用for、什么时候用while比会写循环本身更重要。已知循环次数用for未知次数直到满足条件才停用while。比如“输入正整数计算它是几位数”这道题循环次数取决于数字不断除以10直到变成0显然用while更合适。来看一个容易错的写法——计算末位不为0的数字位数#include stdio.h int main() { int n, count 0; scanf(%d, n); // 考虑边界输入0时位数是1需要单独处理 if (n 0) { printf(1\n); return 0; } // 每次去掉末位直到n归零 while (n ! 0) { count; n n / 10; } printf(%d\n, count); return 0; }逻辑说明除以10的整数运算是“去掉末位”的标准做法循环结束时count就是位数。参数说明if (n 0)是边界保护很多评测数据会包含0漏掉它就会输出0而不是1。刷循环题时如果出现“程序运行时间超限”八成是死循环——比如while里忘了更新条件变量。一个排查技巧是检查循环体内有没有改变循环条件的语句没有的话就是死循环。3. 数组与函数模块从处理单点到处理批量数据3.1 数组专项下标越界与数组初始化的暗坑数组是C语言从“能写小程序”迈向“能写真实工具”的分水岭。习题集里数组相关题目包括逆序存放、查找最大值、排序、矩阵转置等。很多人在这一章开始频繁“答案错误”但看本地输出明明是对的——多半是下标越界导致的内存脏数据。一个典型例子是“将数组元素逆序存放”。新手容易写出从0到n遍历并交换的代码结果发现交换了两次又变回原样。正确做法是只需要遍历前半部分#include stdio.h #define MAXN 100 int main() { int arr[MAXN], n, i, temp; scanf(%d, n); for (i 0; i n; i) { scanf(%d, arr[i]); } // 只需要交换前半部分和后半部分 for (i 0; i n / 2; i) { temp arr[i]; arr[i] arr[n - 1 - i]; arr[n - 1 - i] temp; } for (i 0; i n; i) { printf(%d , arr[i]); } printf(\n); return 0; }逻辑说明i n/2保证每个元素只参与一次交换。n为奇数时中间元素不需要交换这个条件对奇偶数都成立。参数说明MAXN定义为100是最大元素个数实际用n控制循环范围防止越界访问。习题集里有些题没有明确给出数组上限这是评测平台的“压力测试”设计。建议一开始就把数组开大一点或者按题目逻辑推断最大可能值再预留空间。用malloc动态分配也行但竞赛题往往直接开静态数组更稳定。3.2 函数设计参数传递方式决定代码的扩展性函数这章的重点从“让程序跑通”转为“让代码结构清晰”。习题集常见题目有用函数实现数组求和、判断素数、字符串比较等。这里考察两个核心概念值传递和地址传递。值传递是拷贝一份数据给函数函数内修改不会影响主调函数中的原变量。地址传递通过指针让函数直接操作原内存。数组作为函数参数时自动退化为指针所以函数内修改数组元素是“真实修改”。入门阶段最实用的一个写法是求数组平均值#include stdio.h // 数组参数本质是指针不需要返回长度 double average(int arr[], int n) { int i; double sum 0.0; for (i 0; i n; i) { sum arr[i]; } // 整型转换浮点再除避免整数除法丢精度 return sum / n; } int main() { int arr[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double avg average(arr, 10); printf(平均值为: %.2lf\n, avg); return 0; }逻辑说明arr传入时退化为指针sizeof(arr)在函数内不准确所以必须额外传入长度参数n这是C语言的潜规则。参数说明返回值类型用double而非int否则小数部分被截断printf中用%.2lf控制输出精度。函数章节还有一个反直觉的考点static局部变量。在函数内部声明static变量函数退出后值仍保留。习题集里可能有“连续调用函数实现计数器”这类题如果没用static就会得到错误结果。4. 指针、字符串与结构体知识点密度最大的一个台阶4.1 指针运算不要把指针当普通变量来调试指针是C语言劝退率最高的一章。习题集里的指针题核心不是“背诵指针语法”而是在实际调试中理解内存地址的变化。最经典的“字符串长度计算”用指针写法可以帮新手直观理解地址移动的过程#include stdio.h int str_len(const char *s) { const char *p s; // p不断向后移动直到遇见字符串结束符 while (*p ! \0) { p; } // 指针相减得到元素个数 return p - s; } int main() { char str[] hello; printf(长度是: %d\n, str_len(str)); return 0; }逻辑说明p从字符串首地址出发每次加1指向下一个字符循环结束时p指向\0的位置p - s就是字符个数这比用计数器更直观地展示指针移动原理。参数说明const char *s表示函数内部不会修改字符串内容是一种防御式编码习惯。这道题的变体很多比如要求用递归实现、用下标实现、统计某字符出现次数等。练指针时不要只看代码能不能运行要习惯在纸上画出内存布局栈上字符串数组的存储区域、指针变量的指向、\0的位置。这样遇到数组指针和指针数组的区分题时才不会懵。4.2 字符串处理函数边界strcpy、strcat与缓冲区溢出习题集中的字符串题大量涉及gets、strcpy、strcat等库函数。如果只按“功能”去记而不关心边界很容易在评测平台上“答案错误”或“运行时错误”。一个典型是字符串拼接新手用strcat之前没有确认目标数组有足够空间导致越界写入。#include stdio.h #include string.h int main() { char dest[20] Hello; char src[] , World!; // strcat前确认dest剩余空间是否够用 if (strlen(dest) strlen(src) 1 sizeof(dest)) { strcat(dest, src); printf(%s\n, dest); } else { printf(缓冲区空间不足\n); } return 0; }逻辑说明strcat会在dest末尾覆盖\0然后拼接src内容并补一个新\0。如果剩余空间不够写入越界是未定义行为。参数说明sizeof(dest)是数组总容量strlen得到的是已用长度两者之差才是剩余容量加1是给终结符留位置。习题集里某些题指定不能用库函数这时候要自己实现字符串拼接逻辑先找到dest末尾的\0再逐个拷贝src字符到尾部。这个考点本质是让新手理解库函数内部到底做了什么。4.3 结构体与文件读写处理真实数据的起点结构体这一章的题目开始接近真实业务比如学生信息管理、成绩单排序等。核心操作是把不同类型的数据打包在一起再用数组存储多个对象。一个常用模式是结构体数组配合冒泡排序#include stdio.h #include string.h struct Student { int id; char name[20]; double score; }; int main() { struct Student stu[3] { {1001, Alice, 88.5}, {1002, Bob, 76.0}, {1003, Cindy, 92.0} }; int i, j; struct Student temp; // 按成绩从高到低排序冒泡法 for (i 0; i 2; i) { for (j 0; j 2 - i; j) { if (stu[j].score stu[j 1].score) { temp stu[j]; stu[j] stu[j 1]; stu[j 1] temp; } } } for (i 0; i 3; i) { printf(%d %s %.2lf\n, stu[i].id, stu[i].name, stu[i].score); } return 0; }逻辑说明结构体变量之间可以直接赋值temp stu[j]是一次整体拷贝不需要逐个成员复制。参数说明结构体数组按行初始化时每行用一对花括号包裹一个成员字符串常量直接填入字符数组。文件读写题通常要求从文本文件读入学生数据、处理后写回另一个文件。新手很容易忘记fclose导致数据没落盘这在在线评测中不算错但在本地测试时会出现诡异问题程序显示成功但文件内容为空。养成fopen之后立刻检查返回值是否为NULL的习惯同时配对fclose。5. 刷题避坑指南评测平台不会告诉你的四个真相5.1 本地通过但提交“答案错误”多半是头和尾出了问题这个坑我当年踩得很重。在本地编译器里运行输入样例数据输出和题目给的一模一样一提交却显示“答案错误”。反复检查逻辑没问题最后发现是输出格式不对——题目要求输出后跟换行我漏了或者printf格式串里多了个空格。现象本地输出正确但在线评测结果错误。原因评测系统是逐字符比对输出末尾换行、空格、空行都算差异。解决每次提交前看题目输出样例把printf的每个字符都在心里读一遍尤其是逗号冒号空格。另外一个高频原因是从文件读写角度出发的题目要求从文件读入但你写了scanf或者反过来。习题集有些附加题会明确把数据放在文件里这时要用fopen和fscanf组合不能只改输入方式。5.2 数组越界不报错不崩溃只给错误结果很多新手以为数组越界一定会崩溃实际上在C语言里越界读写的是栈上邻近内存可能刚好是另一个变量的位置。现象排序结果里出现随机大数或负数或者某次运行正常、下次运行结果不同。原因访问了arr[n]或arr[-1]这些位置的内容是不确定的。解决写循环时反复核对边界。数组下标从0到n-1看到i n就要警惕。做题时可以先用小规模数据手动跟踪一遍把每个下标值都算出来。5.3 浮点数比较永远不要用“”习题集里有一些几何或数学计算题比如计算两点距离、判断点是否在圆内。现象逻辑看起来完全正确但评测结果不稳定边界测试时有时对有时错。原因浮点数在二进制中无法精确表示0.1 0.2不等于0.3直接判等会失败。解决改用精度判断两个浮点数之差的绝对值小于某个极小值就视为相等比如fabs(a - b) 1e-6。如果题目要求输出保留两位小数比较时也先按同样精度处理。5.4 忽略了“多组输入”的循环读入模式习题集后半部分的题经常暗含“输入包含多组测试数据以EOF结束”这种说明。现象只处理一组数据就输出结果提交后“答案错误”或“运行超限”。原因在线评测会给多组输入你的程序读一组就退出了。解决把主处理逻辑包在while (scanf(...) ! EOF)循环里或者按题目给定的结束标志如输入0终止判断并break。刷到这类题时先观察题目描述“输入多行每行两个整数”基本就是这种模式。6. 用在线评测系统反向验证学习效果从“能通过”到“会变通”刷题到中期有一个很实用的进阶技巧不要只盯着“通过”这个结果而是主动研究题目的边界数据。习题集虽然每道题都有标准答案或测试数据但评测系统往往隐藏了部分边界用例。我习惯的做法是写题之前先在草稿纸上列出所有能想到的边界情况比如输入为0、输入为负数、输入为最大值、输入为空格开头的数据然后逐一套进代码里走一遍。拿“判断闰年”这道经典题来说普通写法是能被4整除但不能被100整除或者能被400整除。但边界年份1900年和2000年很容易测出歧义1900不是闰年2000是闰年。如果没有把这两个值作为测试用例代码很可能只满足“被4整除”这个条件提交后错误。从此之后我养成了一个习惯任何题目写完后先自己构造至少五组极端输入再提交评测。这个习惯在应付结构体和文件操作题时也很管用。另外一个让学习效率翻倍的方法是看“错误输出”而不是只看“通过”。在线评测系统通常会告诉你预期输出和实际输出不要只瞄一眼就关掉。差一个空格、差一个换行都要对着预期输出逐字符比对。我每次刷到字符串操作题都会故意在本地测试时输入带空格和回车混合的数据观察scanf和gets在行为上的差异理解它们分别在什么场景下使用。文件操作题的自测方法也有讲究。题目要求从in.txt读取数据我一般会在本地手动创建一份包含多行数据的测试文件并故意放几行格式不完整的数据比如某行只有两个字段而其他行有三个字段看看程序会不会稳定地跳过或报错。这样能模拟评测数据的“脏”特性比一直用干净样例更接近实战。从那以后我每次做完文件题都会强制走一遍这个流程构造边界数据、确认输出格式、检查文件关闭、提交后再根据结果差额回查。靠这个笨办法我把习题集里绝大多数题目都刷到了“一次通过”的水平。希望这份实战笔记能帮你在C语言这条路上少浪费一些时间把精力真正放到理解和变通上去。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑