简介:《C语言程序设计(第四版)》何钦铭课后习题及答案PDF,面向高校C语言课程学习者、备考学生及自学者,用于课后练习对照、知识点复习与程序题查漏补缺,兼顾课堂作业与考试复习场景。内容覆盖标识符合法性判断、关键字解释、变量声明、顺序与分支循环结构、函数、数组和字符串等基础主题,第一章含标识符与关键字辨析、1—100中能被6整除求和及素数判断流程图,第二章涉及选择题、printf与scanf用法、运算符优先级等常见考点。资源包共1个PDF文件,大小约2.67MB,单文件结构直接查阅,并配有章节习题答案、选择题解析、流程图分析和可验证的参考代码,便于读者梳理解题思路并核对运行结果。目前已有12328人学习下载,适合按章节同步练习、期末复习与易错点复盘。
1. 最容易丢分的,往往是第一题
翻开这本《C语言程序设计》第四版的课后习题及答案,很多人的第一反应是直接翻到数组、指针、函数那几章,觉得前面太简单。结果卷子发下来,扣分的偏偏是第 1 章那道“下列标识符哪些合法”:total、debug、Counter1、begin一眼就过,Large&Tall 里的 & 却被漏掉——它既不是字母,也不是数字或下划线。这类题不考你背了多少语法,考的是基础规则能不能一次判对、代码能不能一次跑对。整份习题的分布很清楚:标识符与关键字、表达式与类型转换、printf/scanf、三种控制结构、循环累加与素数判定,这五块撑起了绝大部分基础分,剩下的算法题靠的是把边界想全。它适合期末冲刺、专升本、计算机二级C语言这类笔试和上机前的突击,也适合已经学过一遍、但一写代码就出错的人回头补基础。
2. 数据类型与表达式:三类必考题的判定流程
2.1 标识符与关键字:两步判定法
判标识符合不合法,只走两步,别凭眼熟。第一步看组成:只能有字母、数字、下划线,出现 &、-、空格、小数点,直接判不合法,所以 Large&Tall 里那个 & 就是唯一的原因。第二步看开头:第一个字符必须是字母或下划线,不能是数字,3x、1abc全废。剩下还有两条隐规则,考卷很喜欢在这里设陷阱:一是大小写敏感,Total和total是两个不同的标识符,Int也不是关键字;二是关键字不能当变量名,写int int;编译直接报错,但int Int;是合法的。
关键字按用途分四块记,比按字母顺序背靠谱得多:
| 分类 | 关键字 | 记忆点 |
|---|---|---|
| 类型声明 | char int float double short long signed unsigned void | 都是“声明一个什么东西” |
| 复合与自定义类型 | struct union enum typedef | 结构体、共用体、枚举、起别名 |
| 存储与限定 | auto static extern register const volatile | 管变量的生存期和可改性 |
| 语句控制 | if else switch case default for while do break continue return goto | 管流程走向 |
| 运算符式 | sizeof | 算长度,长得像函数其实是运算符 |
真题演练:判断total、_debug、Large&Tall、Counter1、begin_是否合法。答案是 total、debug、Counter1、begin合法,Large&Tall 不合法。答题时别只写结论,把理由写全:begin_以下划线结尾没问题,规则限制的是开头;_debug以下划线开头也合法,下划线在开头和结尾都允许。这条“下划线随便放,数字不能站第一位”的口诀,考场上比背定义快。
2.2 整型除法与隐式类型转换:1/k 和 1.0/k 差在哪
这是填空题的高频点:k 是整型变量,1/k的结果是什么类型,1.0/k又是什么类型。答案分别是整型和实型。原因不复杂——两个 int 相除,商还是 int,小数部分直接截掉;1.0是 double,运算前系统先把 k 提升成 double,再按浮点除法算。答题模板就一句:看运算符两侧“最宽”的那个类型,宽的一方决定结果类型。
#include <stdio.h> int main(void) { int k = 4; printf("%d\n", 1 / k); /* 整型除整型:0,小数被截断 */ printf("%.2f\n", 1.0 / k); /* 1.0 是 double,k 先提升为 double */ printf("%.2f\n", (double)1 / k); /* 强制转换写法,效果相同 */ printf("%.2f\n", 1 / (double)k); /* 也可以转分母,转谁都能触发提升 */ return 0; }三个输出分别是 0、0.25、0.25、0.25。参数说明上要盯住格式符:int 用%d,double 用%f或%lf,.2是保留两位小数。这里最常见的失分写法是printf("%d\n", 1.0 / k),格式符和实际类型对不上,输出不可预期,改错题里就等着你去抓。另一个变体是sum / 4求平均:sum 是 int,结果还是 int,必须写成sum / 4.0才能得到小数,教材里求四个整数均值的题就是靠这一手拿分。
2.3 运算符优先级:四档排完,选择题秒杀
优先级不用背全表,记住四档就够应付:单目(++ -- ! (类型) sizeof)最高,然后算术(* / %高于+ -),再关系(< > <= >=高于== !=),再逻辑(&&高于||),赋值=垫底且右结合。真题里问“下列运算符中优先级最低的是”,选项*、=、==、%,答案就是=。判断链条很短:*和%是算术,==是关系,=是赋值,赋值最低。
这条规则衍生出三个常考变体。第一,x = y == 0不是判断 x 等于 0,而是先算y == 0得到 0 或 1,再赋给 x。第二,if (a = 5)能编译通过,表达式的值是 5,恒为真,所以永远走 if 分支——这就是易错清单里的头号陷阱。第三,a = b = c因为右结合,等价于a = (b = c),b 和 c 都被赋值,a 拿到的是 c 的值。答这类题的模板是:先找赋值号把它切两半,左边是接收方,右边整体算完再赋。
再补一个整除细节,选择题常拿它出题:123/100得 1(取商),901%10得 1(取余),76%3得 1,625%5得 0,所以“值不等于 1”的选项落在 625%5 上。别忘负数:C 的整数除法向零截断,-7/2是 -3 不是 -4,-7%3是 -1,余数的符号跟着被除数走。这条在判断题和填空题里都出现过,写答案时顺手带一句“向零截断”,比只给数字稳。
3. 输入输出与程序改错:照单子查,三处必看
3.1 printf 改错题:引号、分号、占位符个数
教材第二章那道经典改错题,程序想显示Welcome to You!,写成了printf(Welcome to You! \n"),问有几处错。答案是两处:字符串少了开头的双引号,语句末尾少了分号,需要改 2 处(选项 B)。答题时按固定顺序扫一遍,比凭感觉快:
- 格式控制字符串有没有完整的一对双引号,
\n要在引号里面; - 语句末尾有没有分号,
printf后面漏分号是最高频的错; - 占位符个数和参数个数是否一致,类型是否对得上;
- 用到 printf 的文件有没有
#include <stdio.h>。
第三点单拎出来练一道:要求输出形如2*3=6的乘法口诀,四个选项里只有printf("%d * %d=%d\n", i, j, i*j);是对的。错误选项分别错在:"d * %d=%d\n"少了 %;printf("%d * %d=%d\n", i, j)三个占位符只给两个参数;printf("%d=%d * %d\n", i, j, i*j)顺序和自己想输出的式子对不上。
#include <stdio.h> int main(void) { int i = 2, j = 3; printf("%d * %d=%d\n", i, j, i * j); /* 3 个 %d 对应 3 个参数,顺序一一对应 */ return 0; }代码本身没什么技术含量,重点是参数对位:第一个%d吃 i,第二个吃 j,第三个吃i*j。改错题给分往往按“处”算,找出问题后要写清“第几行、错在哪、改成什么”,只圈不写理由的,人工阅卷容易扣一半。这也是我后来带人复习时反复强调的:改错题当成三行小作文写。
3.2 scanf 的坑:& 不能丢,顺序不能反,double 必须 %lf
真题原样:x 是 double,n 是 int,要求输入 3 和 1.25 之后得到 x=1.25、n=3,正确的语句是scanf("%d%lf", &n, &x)。判断逻辑是反推输入顺序——先输入 3 给 n,再输入 1.25 给 x,所以格式串里%d在前、%lf在后,变量地址也按同样顺序排。四个选项里,B 是"%lf%d"但参数写成&x, &n,顺序反了;D 的格式串里有逗号,实际输入必须敲3,1.25才匹配,题目说的是输入两个数中间空格,所以不对。
scanf 的规则整理成三条:第一,除字符数组名外,变量前都要加&,漏 & 是最经典的翻车点,编译能过,运行时行为异常;第二,格式串里写了什么字符,输入时就得原样敲什么,"%d,%lf"里的逗号是硬要求;第三,double 在 scanf 里必须用%lf,用%f会读错数据。再提醒一句格式化输入的空格:%d会自动跳过前导空白,所以"%d%d"输入时用空格或回车分隔都行,这也是它比"%d,%d"好用的原因。
字符输入还有一层坑。前面用%d读完数字,缓冲区里通常留下一个回车,紧接着用%c或gets就会读到那个回车而不是你要的字符。处理办法是在%c前加一个空格写成" %c",或者读之前清一次缓冲区。这个点在字符串那一章的习题里出现率高,答题时把“缓冲区残留”四个字写出来,能明显区分出你是真跑过代码的。
3.3 测试用例设计:为什么至少要三组
真题问:为了检查比较两个数大小的 if-else 语句两个分支是否正确,至少需要设计几组测试用例。答案是三组,而且必须覆盖 x>y、x==y、x<y 三种情况,选项 D(3 和 4 输出 4;5 和 5 输出 5;4 和 3 输出 4)就满足。另有一个选项看着也像三组,但其中一组是 5 和 100 输出 100,两组都落在 x<y 里,等于有个分支根本没测到——这就是典型的“用例数量够了、覆盖不够”。
答题模板可以写成三步:先把 if-else 的所有分支列出来,一个分支配一组数据;再补边界值,尤其是“等于”的那个临界点;最后检查每组都写明输入和预期输出。拿阶梯电价那道题练手就很清楚:设月用电量 50 度以内电价 0.53 元,超过 50 度的部分每度加 0.05 元,用电量为负要输出 Invalid Value。这套规则要测的用例至少四组——-1(非法分支)、0(边界)、50(临界,算在低价档)、51(跨档)。少测 50 这组,就永远发现不了“含 50”这个边界条件有没有写错。
判素数的题同理。用例要包含:1(不是素数)、2(最小素数,循环一次都不进)、3、4(合数)、一个较大的素数。很多人只测 7 和 9 就觉得够了,结果 n=2 时循环上界写错、直接判成非素数,一上机就露馅。测试用例这类题不怕写多,怕的是每一条都落在同一个分支里。
4. 三种控制结构与算法题:流程图、素数、累加
4.1 顺序、分支、循环:在一张流程图里认出来
教材第 1 章要求对“判断 n 是否为素数”的流程图,指出哪些是顺序结构、哪些是分支、哪些是循环。答这类题的标准动作是先给流程分段,再给每段贴标签。输入 n、i 赋初值 2,这一段从上到下没有判断也没有回头,是顺序结构;n % i == 0成立就输出“不是素数”并结束,不成立就继续,这是分支结构;i 自增后回到判断条件i < n重新比较,这是循环结构。三段拼起来,顺序打底、分支做判断、循环控次数,就是结构化程序的全部骨架。
答题时要多写一句“循环的终止条件是什么”,这是老师最爱看的区分点。上面这个流程里,循环结束有两种原因:一是找到了能整除的 i,提前跳出;二是 i 一直加到 n 也不整除,正常结束。两种出口对应两个结论,写清楚了,说明你不是背模板。
配套还要会手写 N-S 图或伪代码。伪代码写成这样足够拿分:读入 n;i 赋 2;当 i < n 时重复——若 n 能被 i 整除,输出不是素数,结束;否则 i 加 1;循环结束后输出是素数。注意“结束”这个词的位置,写错一格,逻辑就变成了“找到一个因子还继续找”。
4.2 素数判定:边界、循环上界与标志变量
真题要求判别给定整数 n(n>1)是否为素数。思路是让 n 从 2 开始除,一直除到 n-1,中途被整除就不是素数,一次都没被整除就是素数。落到代码上,有三种写法,考场上优先选标志变量那种,因为它不容易出错。
#include <stdio.h> #include <math.h> int main(void) { int n, i; int is_prime = 1; /* 先假设是素数,找到因子再推翻 */ scanf("%d", &n); if (n <= 1) { is_prime = 0; /* 1、0 和负数都不是素数,先挡掉 */ } for (i = 2; i <= (int)sqrt(n); i++) { /* 只需试到根号 n */ if (n % i == 0) { is_prime = 0; /* 找到因子,立刻定性 */ break; /* 跳出循环,后面的除法没必要再算 */ } } printf("%s\n", is_prime ? "yes" : "no"); return 0; }几处参数和逻辑要说清。sqrt(n)需要math.h,返回值是 double,跟 int 比较时强转一下更稳;循环上界取到根号 n 是因为如果 n 有大于根号 n 的因子,必定对应一个小于根号 n 的因子,试到根号就够了,这一步能把 10000 以内的判断从近万次除法压到一百次以内。break的作用是找到因子就不再空转。is_prime必须初始化,忘了初始化它就是个随机值,程序时对时错,这类 bug 在机试里最耗时间。
第二种写法是计数器法:统计能整除 n 的个数,最后判断个数是否为 0。逻辑直观但多算很多次。第三种是函数封装,把判断抽成int isPrime(int n),主函数只负责输入输出。真题改成“输出 100 以内所有素数”时就该用第三种,主函数里套一层 for,调用完判断再决定打不打印,每行输出 5 个还得加个计数器控制换行。三种写法都能得分,但写函数的那版在扩展题上省一半时间。
4.3 累加与整除筛选:循环三要素加一次手算校验
教材第 1 章那道求 1 到 100 中能被 6 整除的所有整数之和的题,代码只有几行,但把 for 的三要素占全了。
#include <stdio.h> int main(void) { int i, sum = 0; /* sum 必须初始化为 0,否则累加的是垃圾值 */ for (i = 1; i <= 100; i++) /* 初值 1、条件 i<=100、步长 +1 */ if (i % 6 == 0) /* 取余为 0 就是能被 6 整除 */ sum = sum + i; printf("sum=%d\n", sum); /* 运行结果 sum=816 */ return 0; }运行结果是 816。这个数字最好能自己验一遍,考场上算完不放心就这么做:能被 6 整除的数是 6、12、…、96,一共 16 个,等差数列求和 (6+96)×16÷2 = 816,与程序结果一致。手算校验这一步看起来多余,实际是上机题最有效的后悔药——结果对不上,八成是边界写成了i < 100或者 sum 忘了初始化。
同类型的还有序列求和:输入两个正整数 m 和 n,求 Σ(i² + 1/i),结果保留 6 位小数。这题唯一的坑是1/i,两个 int 相除结果永远是 0,必须写成1.0 / i。
#include <stdio.h> int main(void) { int m, n, i; double sum = 0.0; /* 累加浮点结果,用 double 装 */ scanf("%d%d", &m, &n); for (i = m; i <= n; i++) sum += i * i + 1.0 / i; /* 1.0 触发浮点除法,不能写 1/i */ printf("%.6f\n", sum); /* 保留 6 位小数 */ return 0; }参数上强调两点:sum声明成 double 并初始化为 0.0,别用 int;输出格式%.6f的 6 就是题目要求的位数,少了要扣分。求四个整数均值那道题也是同一个套路,average = sum / 4.0配printf("%.1lf\n", average),把 4 写成 4.0 这一笔,就是整道题的分水岭。
5. 易错题清单:五个每年都有人踩的点
5.1 错点一:if (k = 0) 把赋值当条件
错在哪:把if (k = 0)当成“判断 k 是否等于 0”,答成走 if 分支、flag=0。为什么错:C 里=是赋值,整个表达式的值就是赋进去的那个 0,0 为假,于是走 else,flag 被赋成 1。最终输出是k=0#flag=1。怎么答对:先在草稿上把k = 0圈出来,写一句“赋值表达式,值为 0,条件为假”,再写结论;顺带补一句“若要用比较应写k == 0”。编译器对这种写法通常只给警告甚至不报警告,肉眼检查是唯一的防线,这也是它年年上榜的原因。
5.2 错点二:交换两个变量,顺序写反等于白写
错在哪:填空时写成a = b; b = a;,以为交换完成。为什么错:第一条执行完 a 里已经是 b 的值,原来的 a 被覆盖,第二条再把 b 赋成 a,两个变量最后都是原 b 的值。怎么答对:老老实实用第三个变量,temp = a; a = b; b = temp;,三行的顺序一步都不能换,第一行先把 a 存起来,最后一行再把存的值还给 b。教材里还有一道不用临时变量的题:a = a + b; b = a - b; a = a - b;,输入 3 和 2 输出a=2#b=3。这版能得分但要说明代价:加法可能溢出,可读性差,考场上优先写 temp 版,把加减法版当补充答案写在后头。
5.3 错点三:for 结束后 i 到底等于几
错在哪:填空题最后一行输出,很多人只写到i=3#s=1.833就收笔,漏掉循环结束后那次 printf,或者误以为 i 停在 3。为什么错:i++是在循环体执行完、下一轮条件判断之前发生的,i=3 那轮结束后 i 变成 4,条件i < 4不成立退出循环,i 就保留 4,而 s 不再累加。所以完整输出是 i=1#s=1.000、i=2#s=1.500、i=3#s=1.833、i=4#s=1.833。怎么答对:画一张三列表格,列 i、s、是否继续循环,一行一行填。这类题看着繁琐,但填表比在脑子里跑循环可靠得多,尤其是循环变量在循环外还要用的时候。
5.4 错点四:%d 和 %lf 混用,printf 与 scanf 规则不一样
错在哪:printf("%d\n", 1.0 * k),或者 x 是 double 却写scanf("%f", &x)。为什么错:printf 输出 double 用%f,写%lf也能跑;但 scanf 读 double 必须用%lf,用%f会按 float 的长度去写内存,读出来的数是错的。用%d去接一个 double,属于格式不符,输出不可预期。怎么答对:记住“scanf 严格对号,printf 宽松一半”。做题时把变量类型写在旁边,格式符对着类型抄,比凭手感快。涉及小数位数的题还要盯住.1、.6这类精度修饰,题目说保留 6 位小数,写成%.3f直接判错。
5.5 错点五:除法方向与取余符号,负数一进来就乱
错在哪:认为7/2得 3.5,或者认为-7 % 3得 1。为什么错:两个 int 相除结果还是 int,小数被截掉;C 的整数除法向零截断,取余的符号跟随被除数,所以-7/2是 -3、-7%3是 -1。怎么答对:判偶数写n % 2 == 0,别写n / 2 * 2 == n,后者虽然等价但多一步、易错;遇到负数先判符号,或者直接取绝对值再判断,答题时注明“已处理负数情形”。另外浮点数比较不要用==,两个 0.1 相加未必等于 0.2,写fabs(a - b) < 1e-6更稳,这个细节在判断闰年、判零点的题里都吃过亏。
6. 上机与笔试的答题顺序:一套从读题到验算的动作
时间分配上,先按题型切开,比从头往下做省至少十分钟。选择题和填空题是送分区,一道题不该超过一分钟,遇到需要推循环的(比如上面那道 for 结束值的题)先在旁边打个星号跳过,回头再算;程序改错和填空控制在每题三到四分钟;两道算法题各留十到十二分钟,最后五分钟专门验算临界值。
| 题型 | 建议用时 | 交卷前必查 |
|---|---|---|
| 标识符/关键字判断 | 每题 30 秒 | 是否有 &、数字开头、关键字重名 |
| 表达式求值与类型 | 每题 1 分钟 | 整除截断方向、是否写了.0 |
| printf/scanf 改错 | 每题 3 分钟 | 引号、分号、占位符个数与 & |
| 测试用例设计 | 每题 4 分钟 | 分支是否全覆盖、有无临界值 |
| 算法编程题 | 每题 10 分钟 | 变量初始化、循环边界、输出格式 |
分段函数类题有一套固定骨架,拿阶梯电价练一遍就够举一反三:电价分两档,50 度以内(含 50)每度 0.53 元,超出部分每度加 0.05 元,输入为负要提示非法。
#include <stdio.h> int main(void) { double x, y; scanf("%lf", &x); if (x < 0) { printf("Invalid Value!\n"); /* 非法输入单独一支,放在最前面挡掉 */ } else if (x <= 50) { y = 0.53 * x; /* 含 50,所以用 <= 而不是 < */ } else { y = 0.53 * 50 + (x - 50) * 0.58; /* 0.53 + 0.05 = 0.58,只加超出部分 */ printf("电费=%.2f\n", y); } return 0; }两个细节决定这题能不能满分:一是x <= 50的等号,50 度必须落在低价档,写成< 50就会把 50 度算进高价档;二是(x - 50) * 0.58里的减号,忘了减就变成全额按高价算,结果翻倍。第三个细节是前两个分支不用打印电费,只有高价档打印,漏了这一行会出现 50 度以下没输出。写完先用 50、51、-1 三组数据心算一遍,能过就基本没错。
从那以后我每次写完带分支的题,都强制走一遍“边界三连”:0、临界值、临界值加一,再回头检查每个变量有没有初始化、每个输出格式符和类型对不对得上。这套动作花不到两分钟,救回来的分往往比多做一道题的收益高。这份按章节整理好的课后习题及答案,解析和代码都放在一处,对着练一遍比看三遍书管用,需要的话可以拿去照着跑。希望帮到你。
本文还有配套的精品资源,点击获取