ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

C语言程序设计第四版课后习题及答案:零基础入门与刷题指南

C语言程序设计第四版课后习题及答案:零基础入门与刷题指南

简介:这份PDF资料对应何钦铭《C语言程序设计》第四版课后习题,面向高校C语言课程的初学者、备考学生以及需要自查作业的任课教师,用于解决课后练习无参考答案、知识点零散难串联的问题。内容按章节组织,覆盖标识符与关键字、变量声明、流程控制、函数、数组与字符串等基础模块,每道习题均给出结论与解析过程,例如判断标识符合法性、辨析运算符优先级、用for循环求解1至100内能被6整除的整数和,并附完整代码与运行结果,便于读者对照验证自己的思路。资源包共1个文件,为PDF格式,大小约2.67MB,单文件结构,可在电脑或移动端直接翻阅、检索和打印。目前已有12328人学习下载,说明其题目解析经受了较多使用者的检验。对于想按章节系统复习、核对课后答案或准备课程考核的读者,这份资料能提供较完整的题目索引与解析参考,也方便教师作为课堂讲评的对照素材。

1. 先别急着敲代码:这份 C 语言程序设计课后习题及答案,到底是给谁用的

第一次翻开 C 语言程序设计的教材,很多人会卡在同一个地方:第一章的第一道题不让你写程序,只给你五个名字——total、debug、Large&Tall、Counter1、begin,问哪些能当变量名。你盯着看半天,觉得它们长得都差不多,翻到书后也没有解析,只能凭感觉猜一个。这种「题目看得懂、就是不知道对错在哪」的憋闷感,是零基础入门最容易泄气的一关。

这份 C 语言程序设计第四版课后习题及答案,干的正是补上这一环的活:每道题后面跟着解析,把「为什么是这个答案」讲出来,而不是只丢一个选项字母。它适合刚接触编程、正在跟教材自学的人,适合上完课一头雾水的人,也适合把答案对完却不知道自己错在哪的人。接下来不谈大道理,只说清楚零基础该怎么用它、第一步从哪里迈。

2. 先搞懂三件小事:标识符、关键字和变量名怎么起

写程序这件事,说穿了就是「给数据起名字,然后指挥这些名字干活」。名字起错了,后面的每一行都会跟着出问题,所以教材把标识符放在第一章第一节,不是没有道理。这一章把三个最基础的概念拆开讲清楚:什么是标识符、哪些词你不能用、以及怎么用五分钟把「起名字」这件事练到不用想。

2.1 标识符就是「给东西起名字」:规则为什么定得这么死

先打个比方。快递柜给每个格子编号,如果允许编号里带空格、带星号、带表情,扫码枪就认不出来,整个系统立刻乱套。C 语言里的标识符是同一回事:你用来表示变量、函数、数据类型、语句的那些符号,编译器要能一眼认出来,所以字符范围必须收得很窄。

教材里给出的规定只有两条,但每一条都要刻进脑子里:

  • 只能由字母、数字、下划线这三类字符组成;
  • 第一个字符必须是字母或者下划线,不能是数字。

再加一条容易被忽略的:C 语言区分大小写。Total和total是两个完全不同的名字,Main也不是main。很多新手对着代码找半天「我明明定义了」,问题就出在一个字母的大小写上。

拿第一章那道题逐个过一遍,你就明白规则是怎么落地的。total全是小写字母,合法;_debug以下划线开头,合法;Counter1字母开头、后面带数字,合法;begin_以下划线结尾,合法。只有Large&Tall不合法,因为&既不是字母也不是数字,也不是下划线。更要命的是,&在 C 语言里本身就是一个有专门用途的运算符,编译器看到它就知道「这里该接一个变量地址」,你把它塞进名字里,等于在一句话中间插了个标点,机器自然读不通。

提示:中文、空格、@、#、-这些都不要出现在变量名里。某些编译环境可能放行,但换一个环境就翻车,入门阶段不要给自己找这种麻烦。

2.2 关键字是 C 语言自己占用的名字:这几十个词你不能抢

如果标识符是「你给孩子起名」,那关键字就是「这个城市里已经被人注册过的门牌号」,你再喜欢也不能用。教材里列了一长串:auto、break、case、char、const、continue、default、do、double、else、enum、extern、float、for、goto、if、int、long、register、return、short、signed、sizeof、static、struct、switch、typedef、unsigned、union、void、volatile、while。

一口气背下来没有意义,按用途分成四组就好记很多:

分组常见关键字干什么用
数据类型int、char、float、double、short、long、signed、unsigned、void声明变量和函数返回值的类型
语句控制if、else、switch、case、default、for、while、do、break、continue、return、goto控制程序往哪走、走几遍
存储与修饰auto、static、extern、register、const、volatile说明变量存在哪、能不能改
自定义类型struct、union、enum、typedef、sizeof组合数据、算长度、起别名

记住一个判断方法就够了:你在代码里看到这些词,它们永远只表示语言规定的那一个意思,不会是你的变量。所以写int total;没问题,写int int;就是自找麻烦——编译器会认为你在用一个类型名当变量名,直接报错。这也是为什么教材的解析里特意强调「保留字有特定含义和专门用途」,它的潜台词是:这些名字已经被语言拿走了,你只能用,不能占。

2.3 动手:用五步把「起名字」练成条件反射

光看规则记不住,得让手指参与进来。下面这五步,每一步都不超过五分钟,做完你对标识符的判断基本不会错。

第一步,翻到第一章第 1 题,先用手遮住答案,自己给五个名字逐个打勾打叉,再对答案。第二步,把唯一不合法的Large&Tall改造成合法名字,比如写成Large_Tall,你会发现只是把&换成下划线就通了。第三步,自己再编十个名字,故意混进三个不合法的,隔一天回来自己挑错。第四步,把下面这段代码原样敲进你的编程环境,跑一遍。

#include <stdio.h> int main(void) { int total = 0; /* 合法:纯字母 */ int _debug = 1; /* 合法:下划线开头 */ int Counter1 = 2; /* 合法:字母开头,后面带数字 */ int begin_ = 3; /* 合法:下划线结尾没问题 */ /* 下面这行如果放开注释,编译会直接失败 */ /* int Large&Tall = 4; */ printf("%d %d %d %d\n", total, _debug, Counter1, begin_); return 0; }

这段代码里,int是类型,后面四个是变量名,= 0是给变量一个初始值。printf里的%d是占位符,表示「这里要填一个整数」,后面四个变量按顺序填进去,中间用空格隔开。第五步,把注释那行放开,重新编译一次,亲眼看一看编译器停在哪个位置、报了什么。这一步的价值在于:以后你写错名字时,能第一时间认出「哦,这是名字不合法」,而不是对着一屏红字发呆。

真正跑通一次报错,比背十条规则管用。我第一次学的时候就是把&塞进变量名,编译器的提示指在一个我完全没想到的位置,那次之后我对「名字里只能有字母数字下划线」再没记错过。

3. 把第一个程序真正跑起来:从新建文件到屏幕上出现 sum=816

概念懂了,接下来要解决的是另一个问题:怎么让一段代码真的跑起来。很多零基础的人不是不会写,而是卡在「写完放哪、怎么运行」这一步,编辑器里敲完就不知道下一步了。这一章用一个最短的例子把整条链路走通,再顺手把分支结构过一遍。

3.1 为什么第一个例子选「1 到 100 里能被 6 整除的数的和」

教材第一章第 3 题就是这个:求 1 到 100 中能被 6 整除的所有整数的和,并在编程环境中验证结果。它是个绝佳的入门例子,因为短短十行代码里,变量声明、循环、条件判断、取余运算、累加、输出全都用上了,一个都不少;而且结果sum=816是可以自己动手验算的,6+12+18+…+96,加出来正好 816,你不需要盲信程序。

先看代码,再看每一行在干什么。

#include <stdio.h> int main(void) { int i, sum = 0; /* i 是循环变量,sum 用来累加,一定要先清零 */ for (i = 1; i <= 100; i++) /* 从 1 数到 100,包含 100 */ if (i % 6 == 0) /* % 是取余数,余数为 0 说明能被 6 整除 */ sum = sum + i; /* 把符合条件的数加到 sum 上 */ printf("sum=%d\n", sum); /* %d 表示按整数输出,\n 是换行 */ return 0; }

#include <stdio.h>是告诉编译器:我要用输入输出相关的功能,请把对应的库接进来。int main(void)是程序的入口,程序永远从main开始执行,这一点教材第二章选择题里专门考过。sum = 0这一句最容易被新手忽略——如果不清零,sum里存的是内存中残留的垃圾值,结果会离谱到让你怀疑人生。for后面的三部分是「起点;继续的条件;每轮之后做什么」,i++就是i = i + 1的简写。%是取余,==是判断相等,注意是两个等号,一个等号是赋值。

3.2 四步跑通:新建、保存、编译、运行

不同编程环境的按钮位置不一样,但流程完全一致,四步走完就能在屏幕上看到结果。

第一步,新建一个源文件,把上面的代码敲进去。建议手敲,不要复制,手敲能让你真正注意到分号、括号和引号有没有漏。第二步,保存文件,文件名用英文,后缀必须是.c,比如sum6.c。后缀不对,编译器不会把它当 C 语言源文件处理,这是新手第一个隐形坑。

第三步,编译。如果你用的是命令行环境,命令长这样:

# 把 sum6.c 编译成可执行文件 sum6 gcc sum6.c -o sum6

gcc是编译器程序,sum6.c是你要编译的源文件,-o sum6表示输出的可执行文件叫sum6。如果你用的是带界面的集成环境,通常有一个「编译并运行」的按钮,点它效果一样。第四步,运行:

# 运行刚刚生成的可执行文件 ./sum6

屏幕上应该出现sum=816。看到这一行,说明你的环境、代码、编译链路全部通了,这一刻比看懂十页书都值。

3.3 改三个地方,确认你不是在抄代码

代码跑通了不代表懂了。最有效的检验方式是改参数,并且改之前先自己预测结果,再跑一遍对账。

第一个改动,把i % 6 == 0里的 6 改成 7,也就是求 1 到 100 里 7 的倍数之和。动手之前先算一遍:7+14+…+98,一共 14 项,结果是 735。跑出来对上,说明你真的理解了这一行在筛什么。

第二个改动,把i <= 100改成i < 100。很多人以为结果会变,实际上还是 816,因为 1 到 100 之间能被 6 整除的最大数是 96,100 本身不满足条件,去掉它不影响结果。这个观察很有价值:它告诉你边界值要不要包含,取决于边界本身是否满足条件。想看到明显差别,就把 6 改成 5,这时i <= 100得 1050,i < 100得 950,差的那 100 就是边界值。

第三个改动,把sum = 0改成sum = 1,结果会变成 817。数字只差 1,但它证明了一件事:初始值是你亲手设定的起点,不是系统自动帮你归零的。理解这一点,后面写累加、计数、求最大值时都不会再犯迷糊。

3.4 再走一遍分支:把阶梯电价那道题自己写一遍

循环练完,教材第二章第 2 题正好用来练分支:某地居民阶梯电价,月用电量 50 千瓦时以内(含 50)每千瓦时 0.53 元,超过 50 的部分每千瓦时上调 0.05 元,输入用电量算电费,如果用电量小于 0 就输出提示。

#include <stdio.h> int main(void) { double x, y; /* x 是用电量,y 是电费,都用小数 */ printf("input x:"); scanf("%lf", &x); /* %lf 用来读 double,& 不能漏 */ if (x < 0) printf("Invalid Value!\n"); /* 先挡掉非法输入 */ else if (x <= 50) y = 0.53 * x; /* 第一阶梯:全按 0.53 算 */ else y = 0.53 * 50 + (x - 50) * (0.53 + 0.05); /* 超出部分按 0.58 算 */ if (x >= 0) printf("电费 = %.2f 元\n", y); /* %.2f 表示保留两位小数 */ return 0; }

double是带小数的类型,读它必须用%lf,输出用%f或%.2f。scanf里的&是取地址符号,意思是「把读到的值放进这个变量所在的位置」,漏掉它程序大概率会崩。三个分支的顺序有讲究:x < 0必须放最前面,因为后面两个分支都在算钱,负数根本没有意义,先挡掉最省事。else if和else是配套的,一旦前面某个条件成立,后面的分支就不会再执行,所以不用担心x <= 50和x > 50同时命中。

自己测三组数:输入 30,电费应该是 15.90;输入 50,还是 26.50;输入 100,超过部分 50 度按 0.58 算,总共 26.50 + 29.00 = 55.50。三组都对上,这道题才算真的做完。

4. 新手最容易翻车的五个坑:报错信息其实在帮你

写代码出错是常态,不是你没天赋。区别在于,有的人看到报错就慌,有的人知道该往哪看。这一章把零基础阶段最高频的五个坑列出来,每个都按「什么现象、为什么、怎么解决」讲,遇到时照着对号入座就行。

4.1 编译阶段过不去的两个坑

坑一:printf少了双引号或者分号。

现象是编译直接失败,报错行指向printf那一句,提示里常见「expected」之类的字眼。原因很直白:printf要打印的内容是一串字符,字符必须用双引号包起来;同时 C 语言的每条语句末尾要用分号结束。教材第二章选择题第 1 题考的就是这个,给了一段printf(Welcome to You! \n")的代码,问改几处才能正常输出,答案是两处——左边少一个双引号,末尾少一个分号。解决办法是养成一个习惯:每敲完一行,手指停半秒,从行首扫到行尾,看引号成不成对、分号在不在。

坑二:#include后面多加了分号。

现象是编译报出一堆看着莫名其妙的错误,或者提示找不到某个函数。原因在于#include是编译预处理命令,不是普通语句,它的作用是「把另一个文件的内容原样搬过来」,不属于要执行的代码,所以末尾不加分号。教材填空题里专门强调过这一点,但特别容易顺手打上去。解决办法就一个字:删。

4.2 能编译但结果不对的三个坑

坑三:scanf漏了&,或者格式符和变量类型对不上。

现象是程序能编译、能运行,但一输入就崩溃,或者读进来的值完全不对。原因有两层:&是取地址符,告诉程序把数据存到哪个位置,漏了它程序就不知道该往哪写;格式符也要一一对应,int配%d,double配%lf,float配%f,配错了类型,读进来的字节就对不上。解决办法是把scanf那一行和上面的变量声明并排看,一个变量一个变量地对,顺序也不能乱——教材选择题里就有一道专门考这个,输入 3 和 1.25,只有格式符顺序和变量顺序都对的选项才能得到n=3, x=1.25。

坑四:整数除法把小数吃掉了。

现象是写1 / k,当 k 等于 2 时,你期待 0.5,结果得到 0。原因是 C 语言里整数除整数,结果还是整数,小数部分被直接丢掉,不是四舍五入,是砍掉。教材填空题第 1 题问的就是1/k和1.0/k分别是什么类型,答案是整型和实型。解决办法是让参与运算的至少一方是小数:写成1.0 / k,编译器会自动把 k 临时转成小数再除,结果就是 0.5。同理,求四个整数的平均值时,要写sum / 4.0而不是sum / 4,否则平均值会被截断成整数。这个坑极其隐蔽,因为程序不报错,只是答案悄悄错了。

坑五:把赋值当成了判断,写成if (k = 0)。

现象是条件分支永远走同一个方向,或者变量值莫名其妙被改掉。原因是=是赋值,==才是比较。if (k = 0)的实际效果是「先把 0 赋给 k,然后拿 0 当条件」,而 0 在 C 语言里代表假,所以这个条件永远不成立,永远走else。教材填空题里有一道就是这个,最后输出k=0#flag=1。解决办法有两个:一是打开编译器的警告选项,这类写法大多数编译器都会提醒你;二是养成把常量写左边的习惯,写成if (0 == k),万一漏了一个等号,编译器会直接报错,因为常量不能被赋值。我个人更推荐第二种,靠机器的强制检查比靠自己的记性靠谱。

5. 把课后习题当练习册:答案不是用来看的,是用来对账的

资源到手之后,最常见的错误用法是从第一页翻到最后一页,把答案从头看一遍,合上书觉得「都懂了」。这种方式几乎没有留存,因为看懂别人的解法,和能从空白页写出解法,是两种完全不同的能力。这一章讲清楚三种题型分别该怎么用,以及一套能坚持下来的流程。

5.1 三种题型,三种用法

选择题的作用是暴露概念盲点。它的正确用法是:先做,做完只标记对错,不急着看解析。错的题,先自己回想是哪条规则没记牢,再去看解析里对应的那一句话。比如运算符优先级那道题,问*、=、==、%哪个优先级最低,答案是=。如果你选错了,说明脑子里「算术 > 关系 > 赋值」这个顺序还没立起来,那就把这句话抄在题目旁边,比抄整段解析有用。

填空题的作用是抓语法细节。它考的都是很细的东西:#include加不加分号、sqrt属于哪个头文件、偶数怎么用表达式写、交换两个变量中间需要几个临时变量。这类题必须动手写,写不出来说明细节没落地。教材里那道「交换 a 和 b」的填空题就很典型,答案是temp=a; b=temp;,三行的顺序错一行,交换就失败。

程序设计题才是真正练手的地方。它的价值不在答案,而在过程:你能不能从一句中文描述,拆出输入、处理、输出三段,再翻译成代码。教材里的求整数均值、阶梯电价、序列求和,都是这类题,每一道都值得在没看答案之前先自己写一遍,哪怕写得丑。

5.2 一套能坚持下来的四步刷题法

第一步,遮住答案做题,写不出来就写不出来,不要中途翻。第二步,对答案,只打勾打叉,不抄。第三步,每道错题追一句话——不是整段解析,就一句,比如「整型除整型结果还是整型」。第四步,把这句话变成一行能跑的代码,亲眼看着它输出你预期的结果。

第四步最容易被跳过,但它才是转化的关键。举个例子,你从填空题里学到「整型除法会丢小数」,那就写这么一小段:

#include <stdio.h> int main(void) { int k = 2; printf("%d\n", 1 / k); /* 整型除整型:小数被砍掉,输出 0 */ printf("%f\n", 1.0 / k); /* 一方是小数:k 被自动转换,输出 0.500000 */ return 0; }

第一个printf用%d,因为1 / k的结果是整数;第二个用%f,因为1.0 / k的结果是小数。跑一遍,两个数字摆在一起,这个知识点就再也不会忘。比抄十遍解析管用得多。

5.3 自己出测试用例:以「输入两个数,输出较大者」为例

教材第二章选择题第 7 题问:为了检查 if-else 两个分支是否都正确,至少要设计几组测试用例。答案是三组,因为两个数比较只有三种情况——第一个大、两个相等、第一个小。这三种情况都要覆盖到,否则你根本不知道另一个分支有没有写对。

具体怎么设计,看这张表:

输入覆盖的情况预期输出
3 4第一个小于第二个4
5 5两个相等5
4 3第一个大于第二个4

对应的代码可以这么写:

#include <stdio.h> int main(void) { int x, y, max; printf("input x,y:"); scanf("%d%d", &x, &y); if (x >= y) /* 用 >= 而不是 >,两个相等时也能得到正确结果 */ max = x; else max = y; printf("max=%d\n", max); return 0; }

注意判断条件写的是x >= y而不是x > y。如果写>,当两个数相等时会走else分支,虽然结果碰巧也对,但逻辑上多绕了一步;更麻烦的是,一旦你把代码改成「相等时输出提示」,用>就会漏掉这种情况。测试用例的意义就在这里:它逼你把边界情况想清楚,而不是等程序上线后被人发现。

再补一句,三组用例只是最低要求。真实场景里还要试负数、试很大的数、试刚好在边界上的数。比如阶梯电价那道题,50 就是边界,你必须单独测一次输入 50,确认它落在「50 以内」那一档,而不是被算进超出部分。

6. 下一步学什么:把这本书用成一条路线,而不是一堆题

课后习题最怕的用法是「当词典查」——遇到不会的翻一下,翻完就放下。它更适合当路线图:按章节顺序推进,每一章都对应一类必须掌握的能力。下面这条路线是我自己踩过坑之后总结的,供你参考。

阶段要练的能力对应习题类型
第 1 周变量、类型、输入输出、算术运算第一、二章的选择题和填空题
第 2 周分支:if-else、多分支、边界判断阶梯电价、比大小、成绩分档
第 3 周循环:for、while、累加、计数、素数判断1 到 100 求和、序列求和、素数判别
第 4 周函数封装、数组存数据后续章节的程序设计题

顺序不要跳。很多人学完循环就急着啃指针,结果连「数组下标从 0 开始」都没搞清楚,指针只会更痛苦。函数和数组是天然的下一站,因为它们解决的是「代码越来越长、数据越来越多」这两个真实问题,学完立刻能用上。

具体到每天怎么练,我建议一个很小的动作:每学完一节,从这一节的习题里挑一道程序设计题,不看答案,从空白文件开始写,写完编译运行,跑三组自己设计的输入。哪怕只写十行,也比看十页解析强。写不出来的时候再翻答案,翻的时候只找卡住的那一步,不要整段抄。

我第一次学的时候,特别喜欢对着答案点头,觉得每一行都看懂了。直到有一次关掉答案,想写一个「求 1 到 n 的和」,卡在循环变量该从几开始、sum该在哪里清零,才发现自己其实什么都没留下。从那以后我每次翻答案之前,都强制自己先写满一页纸,哪怕写错、哪怕编译不过,也先把想法落到文件里,再去对账。这个习惯帮我省掉了大量「看起来会、一写就废」的时间。

下一步,你不需要买更多资料,也不需要先看懂指针。打开这份习题,从第一章第 1 题开始,把那五个标识符自己判断一遍,再敲出你的第一个sum=816。走完这一步,你就已经越过最难的那道坎了。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进