简介:《C语言程序设计第四版》何钦铭课后习题与答案的PDF,面向高校C语言课程学习者、备考学生及需要夯实语法基础的初学者,可用于课后作业对照、章节复习与自测纠错。资料内容覆盖标识符合法性判断、关键字含义辨析、顺序分支与循环结构、变量声明规则、printf与scanf格式化输入输出、函数调用以及数组和字符串处理等核心知识点,并配有选择题答案与解析、流程图分析、编程题参考代码及运行结果说明,方便读者在练习后理解解题思路、定位易错点并形成规范的编程习惯。包内共1个PDF文件,压缩包约2.67MB,单文件结构便于在电脑或移动端直接查阅。该资料已有12328人学习,适合按章节刷题后复盘概念,也可作为课堂同步练习与考前查漏补缺的辅助材料。
1. 从一道标识符判断题切入:C 语言基础题的知识地图
这套 C 语言程序设计(第四版)课后习题及答案,第一章第一题就问:total、debug、Large&Tall、Counter1、begin哪些合法。很多人扫一眼觉得送分,真做起来却在 Large&Tall 上翻车——&是取地址运算符,不是连接符。这道题恰好是整本书知识地图的入口:往下一层是关键字与数据类型,再往上是表达式与输入输出,往上是分支、循环两种控制结构,最后落到函数、数组、字符串。
这份课后习题的答案适合两类人:正在系统学 C 语言、想把课本例题吃透的人,以及期末或机试前需要快速查漏补缺的人。答案的价值不在“抄代码”,而在每题后面那几句解析——它把“为什么错”写出来了,这才是复习时真正要盯的部分。
2. 词法与数据:标识符、关键字与类型系统的考点分布
C 语言的题,出错往往不是逻辑想不出来,而是词法层就写歪了。这一章把“名字怎么起”“关键字有哪些”“类型怎么转”三件事拆开,它们构成了后面所有题目的地基。
2.1 标识符的三条硬规则与三类典型越界
标识符是 C 语言里用来表示变量、函数、数据类型、语句的符号。教材把它压缩成三句话,背下来就够用:只能由字母、数字、下划线组成;第一个字符必须是字母或下划线;区分大小写。
按这三条去套原题就很干脆:total、debug、Counter1、begin全部合法,Large&Tall 不合法,因为它含了&,而&不在允许字符集里。批改这类题时,三类越界最常见:
| 越界类型 | 反例 | 违反的规则 | 补法 |
|---|---|---|---|
| 混入非法字符 | Large&Tall、a-b | 只允许字母、数字、下划线 | 用下划线代替连接符,写成 Large_Tall |
| 数字开头 | 1st、2count | 首字符必须是字母或下划线 | 改成 first、count2 |
| 撞上关键字 | int、for、case 当变量名 | 关键字有专门用途,不能挪用 | 加前缀或改词,如 int_val、loop_i |
还有一个隐性坑:大小写敏感。Total和total是两个不同的标识符,Main也不是main。写程序时看着像同一个名字,编译器眼里完全不同,这种错误在报错信息里往往只出现一行“未声明的标识符”,找起来很费劲。命名习惯上,教材没有硬性要求,但工程里通常约定变量用小写加下划线、宏名全大写,见名知意比短更重要。
2.2 关键字按用途分四组,比死背 32 个更省力
教材列出的关键字一共 32 个,逐个背很痛苦,按用途分组就顺了。分组之后你会发现,每一组其实对应一类题目。
| 分组 | 关键字 | 记忆抓手 |
|---|---|---|
| 数据类型 | char、int、short、long、float、double、void、signed、unsigned | 声明变量、函数返回值类型 |
| 构造与求值 | struct、union、enum、typedef、sizeof | 自定义类型、算字节数 |
| 存储类别与可改性 | auto、static、register、extern、const、volatile | 决定生存期、链接属性、能否被改写 |
| 流程控制与跳转 | if、else、switch、case、default、for、while、do、break、continue、goto、return | 与三种控制结构一一对应 |
这张表最实用的地方是答题时能快速定位。看到题目问“以下哪个不是关键字”,就在这四组里找;看到“声明只读变量”,直接锁定 const;看到“计算数据类型或变量所占字节数”,锁 sizeof。sizeof 常被误认为函数,它其实是运算符,括号里放类型名或变量名都行,这一点在选择题里反复出现。
值得单独提一句的是 static 和 extern。static 修饰局部变量时改变的是生存期(程序运行期间一直存在),修饰全局变量或函数时改变的是链接属性(只在本文件可见);extern 声明的是“这个东西在别处定义”。这两者常常一起出现在“声明与定义的区别”这类题里,答案往往就藏在“有没有分配存储空间”这句话上。
2.3 类型转换:1/k 与 1.0/k 的分水岭
填空题里有一道几乎必考的题:k 是整型变量,1/k的结果是什么类型,1.0/k又是什么类型。答案是整型和实型。原因不复杂:整型除整型,结果仍然是整型,小数部分被直接丢弃;而1.0是浮点常量,运算前系统会做自动类型转换,把 k 提升为浮点型再除。
#include <stdio.h> int main(void) { int k = 4; printf("1/k = %d\n", 1 / k); /* 整型除整型,结果仍为整型,输出 0 */ printf("1.0/k = %.2f\n", 1.0 / k); /* 1.0 为 double,k 被自动提升,输出 0.25 */ return 0; }这段代码的意义在于把“规则”变成“看得见的输出”。1/k输出 0 而不是 0.25,就是整除截断的直接证据;1.0/k输出 0.25,说明提升发生在运算之前。参数上,%.2f控制保留两位小数,%d对应整型,占位符和参数类型必须对得上,否则输出是随机的。
同一个规则在程序设计题里会变形出现。求四个整数的平均值,如果写average = sum / 4;,结果一定是整数,小数位全丢;写成sum / 4.0才对。判断偶数的表达式n % 2 == 0也是同一套逻辑:取余运算两侧都是整型,余数为 0 即能整除。这两处如果只记结论不理解类型提升,换个场景就又错了。
3. 输入输出与运算符:printf、scanf 与优先级的三层结构
第二章的选择题和填空题,有一半篇幅在考 printf 和 scanf,另一半在考运算符优先级。这两块是“看着会、写就错”的典型区域,需要把规则拆到参数级别去看。
3.1 printf 的格式串:双引号、占位符与参数一一对应
教材给的原型是printf(格式控制字符串, 输出参数1, ..., 输出参数n)。三条硬性要求:格式串必须用双引号包起来;语句末尾必须有分号;占位符的个数、顺序、类型要和后面的参数严格对应。
原题里那段printf(Welcome to You! \n")需要改两处,答案选 B:补上开头的双引号,补上末尾的分号。这题看着简单,但它把 printf 最容易漏的两个符号一次考完了。
#include <stdio.h> int main(void) { int i = 2, j = 3; /* 三个占位符对应三个参数,顺序不能乱 */ printf("%d * %d=%d\n", i, j, i * j); return 0; }输出是2 * 3=6。这段代码对应的选择题里,干扰项分别漏了参数、少了占位符、把参数顺序调换,本质上都在考同一件事:占位符和参数是按下标一一对应的。%d只认整型,用%f接 int 会得到无意义的值;反过来用%d接 double 也一样。
常用占位符整理成一张小表,复习时对着看比翻书快:
| 占位符 | 对应类型 | 常见写法 | 说明 |
|---|---|---|---|
| %d | int | printf("%d", n) | 十进制整数 |
| %f / %lf | float / double | printf("%.3f", x) | 默认 6 位小数,.3表示保留 3 位 |
| %c | char | printf("%c", ch) | 单个字符 |
| %s | 字符数组 | printf("%s", str) | 字符串,遇\0结束 |
| %% | 无 | printf("100%%") | 输出一个百分号 |
\n是转义字符,表示换行,它放在格式串内部,不属于参数。输出结果里每一行末尾有没有换行,机试题的比对往往是逐字符的,少一个\n就判错。
3.2 scanf 的取地址与类型匹配
scanf 的原型和 printf 对称,但多了一个硬性要求:普通变量前面必须加&取地址。原题里 x 是 double、n 是 int,输入顺序是 3 和 1.25,正确写法是scanf("%d%lf", &n, &x)。这里有两层对应关系:占位符顺序对应输入顺序,占位符类型对应变量类型。
%lf在 scanf 里是专用于 double 的,float 要用%f;这一点和 printf 不同,printf 里%f和%lf对 double 都能用。这个不对称是历年选择题的高频点,记的时候可以想成“scanf 要按变量的实际大小去写内存,所以必须区分 float 和 double”。
格式串里的分隔符也有讲究。写成"%d,%lf",输入时就必须敲一个逗号;写成"%d %lf",空格、制表符、换行都算分隔。原题的干扰项里就有加逗号的版本,输入3 1.25时会读失败,n 拿到 3,x 没读进去,后面所有输出都跟着错。这类错误在调试时表现为“变量值莫名其妙”,实际原因在输入格式串。
scanf 的返回值是成功读入的项数,这一点常被忽略。写循环读入时用while (scanf("%d", &n) == 1)可以防止读到文件尾还继续跑,机试里很实用。
3.3 运算符优先级:算术 > 关系 > 赋值
教材第二章只介绍了三类运算符:算术、赋值、关系,优先级从高到低就是算术、关系、赋值。题目问“下列运算符中优先级最低的是”,*、=、==、%四个里,=是赋值运算符,最低。
| 优先级 | 类别 | 代表运算符 | 结合方向 |
|---|---|---|---|
| 高 | 算术运算符 | */%+- | 从左到右 |
| 中 | 关系运算符 | <<=>>===!= | 从左到右 |
| 低 | 赋值运算符 | =+=-=*=/= | 从右到左 |
赋值运算符的结合方向是从右到左,所以a = b = c等价于a = (b = c),先把 c 赋给 b,再把 b 的值赋给 a。这个特性在连续赋值时有用,但也容易写出if (a = b)这种把判断写成赋值的错误。
同章还有一组表达式求值题,考的是整除和取余的区别:
#include <stdio.h> int main(void) { printf("%d\n", 123 / 100); /* 取商,结果 1 */ printf("%d\n", 901 % 10); /* 取余,结果 1 */ printf("%d\n", 76 % 3); /* 取余,结果 1 */ printf("%d\n", 625 % 5); /* 取余,结果 0 */ return 0; }四个式子里前三个值都是 1,只有625 % 5是 0,题目问“值不等于 1 的是”,答案就出来了。做这类题不要心算跳步,把商和余数分别写出来再对照,比凭感觉靠谱。
4. 控制结构:顺序、分支、循环三件套怎么拆题
教材明确写了 C 语言三种基本控制结构:顺序结构、分支结构、循环结构。第二章后面的程序设计题,基本都能归到这三类里,认准类型再动手,比直接敲代码快。
4.1 顺序结构:交换变量的两种写法与临时变量
顺序结构是最容易轻视的一块,典型题是交换两个变量的值。填空题给了一半代码,让补temp = a;和b = temp;,思路是借第三个变量中转:先把 a 存进 temp,再把 b 赋给 a,最后把 temp 里的原 a 值赋给 b。
#include <stdio.h> int main(void) { int a = 3, b = 2, temp; temp = a; /* 先把 a 的值存起来,避免被覆盖 */ a = b; /* 此时 a 变成 2 */ b = temp; /* b 拿到原来的 3 */ printf("a=%d#b=%d\n", a, b); return 0; }不加 temp 直接写a = b; b = a;是经典错误,两步之后两个变量都是原 b 的值。教材还给了另一种不用临时变量的写法,靠加减运算完成交换:
a = a + b; /* a 变成两数之和 */ b = a - b; /* b 拿到原来的 a */ a = a - b; /* a 拿到原来的 b */输入 3 和 2,输出a=2#b=3。跟踪一遍:第一步 a=5、b=2;第二步 b=5-2=3;第三步 a=5-3=2。这种写法在笔试题里很常见,但它有溢出风险——两个大整数相加可能超出 int 范围,工程代码里还是老老实实用 temp。
4.2 分支结构:if-else 的花括号与测试用例设计
if-else 的语法点集中在两处:花括号必须成对,else 与最近的未配对 if 结合。原题让补全一段 if-else,正确选项是}else{,因为前面已经有if(x>=y){,中间必须先把 if 的块闭合,再开 else 的块。
#include <stdio.h> int main(void) { double x, y; printf("input x:"); scanf("%lf", &x); if (x < 0) printf("Invalid Value!\n"); /* 非法输入优先拦截 */ else if (x <= 50) printf("fee = %.2f\n", 0.53 * x); /* 第一档,含 50 */ else printf("fee = %.2f\n", 0.53 * 50 + (x - 50) * 0.58); /* 超出部分加价 */ return 0; }这段对应阶梯电价的程序设计题。月用电量 50 千瓦时以内(含 50)按 0.53 元计价,超过部分每千瓦时上调 0.05 元,即 0.58 元;用电量小于 0 输出非法提示。三个分支的边界要抠清楚:x <= 50包含 50,所以 50 走第一档;x > 50走第二档,超出部分才加价,不是全部加价。这是本题最大的坑,写成x * 0.58就全错了。
配套的选择题还考了测试用例设计:比较两个数的大小,至少要三组输入,分别覆盖 a>b、a=b、a<b。四个选项里只有一组同时包含了 3 和 4、5 和 5、4 和 3 这三种情况。这个思路值得迁移到所有分支题上——写完之后问自己一句“等号那一边测过没有”。
4.3 循环结构:for 求 1~100 能被 6 整除之和与累加陷阱
原题要求把流程图改写成程序,求 1 到 100 之间所有能被 6 整除的整数之和。代码只有几行,但三个细节都在扣分点上。
#include <stdio.h> int main(void) { int i, sum = 0; /* 累加器必须初始化为 0 */ for (i = 1; i <= 100; i++) /* 边界取到 100,用 <= */ if (i % 6 == 0) sum = sum + i; /* 满足条件才累加 */ printf("sum=%d\n", sum); return 0; }运行结果是sum=816。可以手算验证:6+12+…+96 等于 6×(1+2+…+16),即 6×136=816,对得上。三个细节分别是:sum 不初始化会拿到垃圾值;循环条件写成i < 100会漏掉 96 之后判断不到 100;i % 6 == 0里的==写成=就变成赋值,条件恒为真(只要赋的值非零),结果会把 1 到 100 全加起来。
填空题里还有一道 for 循环打印题,值得单独看:
int i; double s = 0; for (i = 1; i < 4; i++) { s = s + 1.0 / i; printf("i=%d#s=%.3f\n", i, s); } printf("i=%d#s=%.3f\n", i, s);输出四行:i=1 时 s=1.000;i=2 时 s=1.500;i=3 时 s=1.833;循环结束后 i 变成 4,再打印一次i=4#s=1.833。这题考的是循环变量的终值——for 循环结束后 i 是第一个不满足条件的值,不是最后一个参与循环的值。很多人只写三行输出,漏掉最后一行。
4.4 综合题:素数判定与序列求和的结构分析
素数判定题要求先画流程图,再指出哪些是顺序、分支、循环结构。思路是让 n 从 2 开始除,一直除到 n-1,中途只要被整除一次就说明不是素数。流程图里,读入 n、初始化 i 是顺序结构;判断能否整除是分支结构;i 递增并回到判断是循环结构。三种结构在一张图里同时出现,这正是教材想让你看清楚的。
#include <stdio.h> int main(void) { int n, i, flag = 1; /* flag 先假设是素数 */ scanf("%d", &n); for (i = 2; i < n; i++) /* 从 2 试到 n-1 */ if (n % i == 0) { flag = 0; /* 找到一个因子就能定论 */ break; /* 提前退出,不必再试 */ } if (flag) printf("%d is prime\n", n); else printf("%d is not prime\n", n); return 0; }flag 这种“先假设、再推翻”的写法在判断题里很通用,注意 break 只能跳出最内层循环,如果写成双重循环,break 跳不出外层,需要靠 flag 配合。另外 n 从 2 开始时循环体一次都不执行,flag 保持 1,判定为素数,这个边界是对的;n 为 1 或负数时这套逻辑不成立,题目已经限定了 n>1。
序列求和题给的是从 m 到 n 的累加,通项是 i² 加 1/i,结果保留 6 位小数:
#include <stdio.h> int main(void) { int m, n, i; double sum = 0.0; scanf("%d%d", &m, &n); /* 保证 0 < m <= n */ for (i = m; i <= n; i++) sum += i * i + 1.0 / i; /* 1.0/i 保证是浮点除法 */ printf("%.6f\n", sum); return 0; }1.0 / i不能写成1 / i,否则整数除法得 0,整个通项只剩 i²。sum 声明为 double 而不是 int,否则小数部分全丢。保留 6 位用%.6f,这里 printf 用%f接 double 是合法的。
5. 易漏与易错:八类高频翻车点及补法
做完前四章的题会发现,错的地方高度集中。这一章把最容易漏、最容易错的几类单独拎出来,每条按“漏在哪、为什么漏、怎么补”写,复习时可以直接当检查表。
5.1 语法层:符号缺失与花括号错配
易漏点一:printf 的双引号和分号。为什么漏:写代码时注意力全在内容上,格式串的边界符号是全篇最不起眼的部分,眼睛会自动跳过。怎么补:写完任何一条 printf,先把光标移到行尾看有没有分号,再看第一个参数有没有被双引号完整包住。原题那段printf(Welcome to You! \n")就是双引号和分号各缺一个,改两处,答案 B。养成“先检查壳、再看内容”的顺序,比通读一遍有效。
易漏点二:if-else 的花括号。为什么漏:单行语句可以省略花括号,一旦分支里写了多条语句还沿用省略写法,逻辑就散了;补全题里还容易只写else忘了闭合前面的if块。怎么补:统一风格——只要出现 else,if 和 else 两个块都用花括号包住,哪怕里面只有一行。原题正确选项是}else{,它同时完成了“闭合 if 块”和“开启 else 块”两件事。
易漏点三:预处理命令末尾加分号。为什么漏:整章都在训练“语句以分号结尾”,手一滑就给#include <stdio.h>补上分号。怎么补:记住预处理命令是给预处理器看的,不是 C 语句,行尾不加分号。数学库用#include <math.h>,输入输出用#include <stdio.h>,开平方的 sqrt 就来自 math.h,写sqrt(s*(s-a)*(s-b)*(s-c))时别忘了这个头文件。
5.2 语义层:赋值当判断、整除截断与循环终值
易错点四:把==写成=。为什么错:if (k = 0)在语法上完全合法,编译器不报错,运行时先把 0 赋给 k,整个表达式的值是 0,按“假”处理,于是走 else 分支,flag 被赋成 1,最终输出k=0#flag=1。怎么补:凡是 if、while 的条件里出现赋值号,先停下来确认是不是笔误;习惯上把常量写在左边,写成if (0 == k),漏写一个等号会直接编译报错,把运行期错误提前到编译期。这个习惯叫“Yoda 条件”,土但管用。
易错点五:整除丢小数。为什么错:sum / 4、1 / k、1 / i三处写法不同,错因是同一个——两侧都是整型,结果被截断。怎么补:只要预期结果带小数,参与运算的至少一侧必须是浮点,写成sum / 4.0、1.0 / k、1.0 / i。判断偶数用n % 2 == 0,这里要的恰恰是整型取余,别顺手改成浮点。
易错点六:循环变量的终值与循环后的打印。为什么错:脑子里跟踪循环时只记“循环体内发生了什么”,不记“循环是怎么退出的”。for (i = 1; i < 4; i++)退出时 i 已经是 4,循环体最后一次执行是 i=3,所以循环外那次打印是i=4#s=1.833,s 不再变化。怎么补:跟踪 for 循环时在纸上画三列——i 的值、条件是否成立、s 的值,退出的那一行也写出来。凡是循环后面还有 printf 的题,先问一句“这里打印的是循环内还是循环外的变量”。
易错点七:scanf 的占位符与取地址。为什么错:printf 用多了会形成“占位符随便写”的错觉,scanf 却要求类型严格对应并且必须取地址。输入 3 和 1.25 时,n 是 int、x 是 double,正确写法是scanf("%d%lf", &n, &x);顺序反了、%lf写成%f、漏掉&,三种错法在编译期都不报错。怎么补:写完 scanf 逐个核对三件事——顺序对不对、类型对不对、有没有&(字符数组名本身是地址,例外)。
易错点八:分支边界值的取舍。为什么错:阶梯电价这类题,边界点 50 到底算哪一档,光看文字容易滑过去。怎么补:把边界值单独列成测试用例,50、50.1、-1 三个输入各跑一遍,看输出是否符合题意;条件写成x <= 50和x > 50,两段刚好接上,不留缝也不重叠。
6. 复习清单:把课后题当成检查表用
复习这套题最有效的方式,不是从头再做一遍,而是按题型建一张自检表,每做错一道就在对应行打个记号,第二轮只看有记号的行。
| 题型 | 核心考点 | 自检动作 |
|---|---|---|
| 标识符判断 | 字符集、首字符、大小写 | 逐字符扫一遍,看有没有&、-、$ |
| 关键字辨析 | 32 个关键字分组 | 按数据类型、构造、存储、控制四组回忆 |
| 表达式求值 | 整除、取余、优先级 | 商和余数分别写出来,不跳步 |
| printf / scanf | 双引号、分号、占位符、取地址 | 数占位符个数,对参数类型,查& |
| 分支题 | 花括号、边界、测试用例 | 补三组用例覆盖 >、=、< |
| 循环题 | 初值、终值、累加器 | 纸上跟踪一轮,写出退出时的变量值 |
| 程序设计题 | 类型选择、输出格式 | 检查除法有没有.0,格式符位数对不对 |
具体操作可以这样安排:第一轮按章节顺序过,错题只记题号不抄答案;第二轮只做错题,做完口述一遍错因;第三轮挑程序设计题,关掉答案从空白文件开始敲,跑通再比对输出格式。第三轮最容易暴露问题——思路对但%.1f写成%f、少一个换行,机试判题就是不给分。
有几段代码建议手敲到能默写的程度:求 1 到 100 能被 6 整除之和、素数判定、序列求和、阶梯电价。这四段覆盖了累加器初始化、flag 标记、整除转浮点、多分支边界四个高频点,敲熟之后同类题基本是换汤不换药。
我自己的教训是:早年做这类课后题,总想着“看懂了就是会了”,结果一到上机,scanf("%d%lf", &n, &x)里把&x写成x,程序直接崩,找了三刻钟才发现问题。从那以后我每次写输入语句,都强制走一遍“顺序、类型、取地址”三查,写完再跑一组边界值。这个习惯比多做十道题都值。希望帮到你。
本文还有配套的精品资源,点击获取