CC语言通关
第04课 · 选择结构程序设计
教材对照
第4章 · 书页 83–109

第04课 · 选择结构程序设计

if 三种形式、关系与逻辑运算、条件运算符、switch 多分支

9 个小节 61 段代码 建议 2h
教材依据第4章《选择结构程序设计》书页 p.83–109
本课覆盖:教材第4章正文 + 书末习题(p.107–109)。标「教材 p.X」= 教材段落总结;「例题」= 教材例题;「习题」= 书末题目;「本课补充」= 非教材原文。
在全书对照表中查看 →

教材《C程序设计》第五版(谭浩强)第4章,书页 83–109,共 27 页|建议 2h


课前一句话

📖 本课补充 · 非教材原文

前三章的程序都是"一条道走到黑":从上往下,一句接一句,不用动脑子。这一课给程序装上眼睛——让它先看路牌,再决定走哪条路。学完这一课,你能亲手写出:判断闰年、成绩分档、解一元二次方程(含无实根、复根)、按距离算运费、四个数排序、判断点是否落在圆塔里。全章只有两个新句型:if 和 switch。别的都是给它们配的"路牌写法"。

一、先补地基:本章要解决的问题

📖 本课补充 · 非教材原文

先回忆第 3 章:你写的程序是这样的——

c
scanf("%f", &r);
s = 3.14159 * r * r;
printf("%f", s);

不管输入什么,这三句都要依次执行。这叫顺序结构:无条件,不判断,一条直路。

可现实中到处是岔路口:

  • 如果 x ≥ 0,输出 x;否则输出 −x。(求绝对值)
  • 如果考试及格,放假;否则补考。
  • 如果 b²−4ac ≥ 0,算实根;否则打印"无实根"。

这些事,顺序结构做不到——它不会"看情况"。

所以本章要解决的就一句话:让程序在岔路口先看路牌,再决定走哪条路。

整章我就用一个比喻贯穿到底:你开车,程序就是你,条件就是路牌。

现实里的开车C 语言里对应的东西
一条直路,没有岔口顺序结构
前方 Y 形岔路,必须二选一if ... else ...
路口只有一条匝道,可下可不下if (...) 单分支
岔路里还有岔路(匝道再分叉)嵌套 if
高速上一连串出口,从第一个满足的出去if ... else if ... else if ...
一排带编号的停车位,直接开到对应编号,进门后不刹车就一路开到下一个位switch ... case ... break
路口立一块牌子,一句话说清走哪边条件运算符 ? :

C 语言的选择语句只有两种:if 语句(管两个分支)和 switch 语句(管多个分支)。其余全是这两位的变体。

本章没有带 * 的选学节,4.1–4.8 八个小节全部在考试范围内。


二、知识点讲解

4.1 选择结构和条件判断

📖 教材 p.83–84 · 正文总结

一句话:选择结构,就是程序先算一个条件,根据结果是"真"还是"假",从两条(或多条)路里挑一条走。

拆名字:选择 = choose,结构 = structure;条件 = condition,判断 = judge。合起来:按条件挑一条路的结构。

生活比喻:导航开车到岔路口。路牌写着"上海 / 沈阳",你根据目的地选一条。选完就沿着这条路一直开,不会再回头走另一条。

怎么用:C 里靠 if 和 switch 实现。本章先讲 if 管双分支,再讲 switch 管多分支。

关键理解:if 后面括号里放的是一个表达式。C 不管这个表达式"长什么样",只看它算出来的值:

  • 值非 0 → 当作"真",执行 if 后面的语句;
  • 值等于 0 → 当作"假",跳过(有 else 就去 else)。

考点 if 括号里的表达式可以是关系表达式、逻辑表达式,甚至就是一个变量或一个数。书上原话:"可以是一个数值表达式"。

易错 很多同学以为 if 括号里只能写 a > b。写 if (x) 完全合法,意思是"x 不等于 0 就执行"。

4.2 用 if 语句实现选择结构

📖 教材 p.85–88 · 正文总结

4.2.1 用 if 语句处理选择结构举例

📖 教材 p.85–86 · 正文总结

书上用三个例子开路:例 4.1(解方程,判断有没有实根)、例 4.2(两个数排序)、例 4.3(三个数排序)。三个例子都围绕同一件事:先比较,再决定要不要交换 / 要不要输出。

这里先把例 4.2、4.3 里最值钱的那个动作讲透:交换两个变量的值。

生活比喻:两个杯子,A 杯装醋,B 杯装酱油,要互换。你不能直接把 A 倒进 B——B 里的酱油就没了。必须拿第三个空杯子 C:A→C,B→A,C→B。

代码三件套(考点,背下来):

c
t = a;      /* 先把 a 存进临时杯 t */
a = b;      /* 再把 b 倒进 a,此时 a 原来的值已在 t 里,不会丢 */
b = t;      /* 最后把 t 里存的原 a 值倒进 b */

易错 写成 a = b; b = a; 是错的:第一句执行完,a 已经变成 b 的值,第二句再把 b 赋给 b,两个变量都成了原来 b 的值,a 的原值永久丢失。

4.2.2 if 语句的一般形式

📖 教材 p.87–88 · 正文总结

一句话:if 语句的完整形式是"如果……就……否则……",其中"否则"那一半可以不要。

拆名字:if 英文就是"如果";else 英文是"否则"(else = 别的、另外)。

一般形式(书上原文):

c
if (表达式) 语句1
[else 语句2]

方括号表示可选:else 可有可无,语句 1、语句 2 都可以是简单语句、复合语句(花括号包起来的一坨),也可以是另一个 if。

三种常用形式(考点,考试让你"写出 if 的三种形式",照这个写):

形式写法走法别称
1if (表达式) 语句1条件真就做,假就跳过单分支
2if (表达式) 语句1 else 语句2二选一,必走其一双分支
3if (表达式1) 语句1 else if (表达式2) 语句2 ... else 语句n从上往下挨个试,谁先真走谁多分支(本质是嵌套)

生活比喻:形式 1 是"前方有服务区,想进就进";形式 2 是"Y 形路口,必须选一条";形式 3 是"高速一连串出口,1 号出口没到就继续开,到了 2 号出口满足条件就出去,后面的出口就不再经过了"。

书上 6 条说明,我挑最要命的 4 条:

  1. 整个 if 可以写一行,也可以写多行;但提倡锯齿形缩进,否则自己都看不懂。
  2. 内嵌语句末尾必须有分号——分号是语句的一部分,不是 if 的。
  3. if 无论写几行,都是一个整体、一个语句。别看见分号就以为 if 结束了。
  4. 内嵌语句可以是复合语句,复合语句要用花括号括起来。

易错三大经典错误:

c
if (x > 0);              /* 错:多了分号,if 管的是一个空语句 */
    printf("正数");       /* 这句和 if 无关,永远执行 */

if (x > 0) y = 1         /* 错:y=1 后面少分号,编译报错 */
else y = -1;

else y = -1;             /* 错:else 不能单独出现,它必须跟着 if */

第 1 条特别阴:编译能过,结果全错。if 后面直接跟分号 = 条件白判断了。

4.3 关系运算符和关系表达式

📖 教材 p.89 · 正文总结

4.3.1 关系运算符及其优先次序

📖 教材 p.90 · 正文总结

一句话:关系运算符就是"比较大小"的六个符号,比较的结果只有"真"或"假"。

拆名字:relation(关系)+ al(形容词后缀)→ relational(关系的)。中文教材叫"关系运算",其实就是"比较运算"。

六个运算符(考点,必须背):

运算符含义优先级
<小于高(前四个同级)
<=小于或等于高
>大于高
>=大于或等于高
==等于低(后两个同级)
!=不等于低

三条优先级规则(书上原文):

  1. 前 4 个(< <= > >=)同级,后 2 个(== !=)同级;前 4 个高于后 2 个。
  2. 关系运算符的优先级低于算术运算符。
  3. 关系运算符的优先级高于赋值运算符。

一句话记:算术 > 关系 > 赋值。

所以 c > a + b 等价于 c > (a + b);a = b > c 等价于 a = (b > c)。

易错 a > b == c 这种写法:因为 > 优先级高于 ==,等价于 (a > b) == c,不是 a > (b == c)。

4.3.2 关系表达式

📖 教材 p.90 · 正文总结

一句话:用关系运算符把两个数值(或数值表达式)连起来的式子,叫关系表达式;它的值只有 1(真)或 0(假)。

生活比喻:两块路牌比大小。比完只给一个结论:"是"或"不是",不会给你一个数字。

怎么用:

c
5 > 3        /* 真,值为 1 */
5 == 3       /* 假,值为 0 */
a + b > b + c
(a = 3) > (b = 5)   /* 合法:先赋值,再比较 */
'a' < 'b'           /* 合法:比的是 ASCII 码 */
(a > b) > (b < c)   /* 合法:拿两个 0/1 再比一次 */

考点关系表达式的值参与运算时,真就是 1,假就是 0。

例:设 a=3, b=2, c=1,则 d = a > b 执行后 d 的值是 1;f = a > b > c 的值是 0——因为 > 是自左至右结合,先算 a > b 得 1,再算 1 > c 即 1 > 1,得 0。

易错把数学写法直接搬进 C:

c
if (0 < x < 10)      /* 错!等价于 (0<x)<10,无论 x 多大都为真 */
if (x > 0 && x < 10) /* 对:两个条件都要写全 */

易错== 写成 =(本章第一大坑):

c
if (a = 5)      /* 这是赋值!a 被赋成 5,表达式的值也是 5,恒为真 */
if (a == 5)     /* 这才是比较 */

防错写法:把常量写左边,if (5 == a)。万一漏写一个等号,5 = a 会直接编译报错,错误当场暴露。

易错浮点数不要直接用 == 比较:0.1+0.2 在计算机里不等于 0.3(有微小误差)。正确写法:

c
if (fabs(a - b) < 1e-6)   /* 差得足够小,就认为相等;fabs 要包含 math.h 头文件 */

4.4 逻辑运算符和逻辑表达式

📖 教材 p.90–93 · 正文总结

4.4.1 逻辑运算符及其优先次序

📖 教材 p.91 · 正文总结

一句话:逻辑运算符用来把几个条件拼成一个复合条件——"并且""或者""不"。

拆名字:logic(逻辑)+ al → logical。中文叫"逻辑运算",大白话就是"组合条件"。

三个运算符(考点):

运算符读法含义几目
&&逻辑与(AND)两边都真才真,有一个假就假双目
||逻辑或(OR)有一个真就真,两个都假才假双目
!逻辑非(NOT)真变假,假变真单目

生活比喻:

  • && = 两道闸门串在一起,两道都开才通车("星期六 并且 不下雨,才去公园")。
  • || = 两条路并联,任一条通就能走("年龄小于 12 或 大于 65,可以买优惠票")。
  • ! = 把路牌翻个面,"是"变"不是"。

优先级(考点,书上图 4.6):

!(最高) → 算术运算符 → 关系运算符 → && 和 || → 赋值运算符(最低)

注意两个细节:

  1. ! 比算术还高!所以 !a + b 是 (!a) + b。
  2. && 和 || 虽在同一档,但 && 优先于 ||(书上原文:"!(非) → &&(与) → ||(或)")。

真值表(考点,背):

aba && ba || b!a
真真真真假
真假假真假
假真假真真
假假假假真

4.4.2 逻辑表达式

📖 教材 p.92–93 · 正文总结

一句话:用逻辑运算符把关系表达式(或其他逻辑量)连起来的式子,叫逻辑表达式;结果只有 1 或 0。

核心规则(必须分清两件事):

  • 算出来的结果:只可能是 0 或 1。
  • 拿来当操作数的东西:0 当假,任何非 0 都当真。

所以 4 && 2 的值是 1(不是 2,也不是 4);!4 的值是 0;!0 的值是 1。

考点短路求值(本章最值钱的考点,考试必考):

  • a && b && c:先看 a,a 为假就直接得 0,b、c 根本不算。
  • a || b || c:先看 a,a 为真就直接得 1,b、c 根本不算。

书上那个经典例子,一定要会:

c
a = 1; b = 2; c = 3; d = 4; m = 1; n = 1;
(m = a > b) && (n = c > d);
/* 结果:m = 0,n 仍然是 1 */

为什么 n 不变?因为 a > b 是 0,m 被赋成 0;&& 左边已经是假,右边 (n = c > d) 压根不执行,n 保持原值 1。

生活比喻:两道闸门串联,第一道关着,你根本不用跑去看第二道——反正过不去。

怎么用(闰年,本章必背表达式):

闰年条件:能被 4 整除且不能被 100 整除,或者能被 400 整除。

c
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
    printf("%d is a leap year\n", year);

反过来判非闰年,书上给了两种等价写法:

c
if (!((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
if ((year % 4 != 0) || (year % 100 == 0 && year % 400 != 0))

易错把 && 写成 &、|| 写成 |:& 和 | 是位运算(本书后面才讲),虽然能编译,但含义完全不同,而且不会短路。

易错! 的作用范围:!a > b 是 (!a) > b,不是 !(a > b)。要否定一整个条件,必须加括号:!(a > b)。

4.5 条件运算符和条件表达式

📖 教材 p.94–95 · 正文总结

一句话:? : 是 C 里唯一的三目运算符,能把一个简单的 if-else 压成一句话。

拆名字:条件运算符由 ? 和 : 两个符号组成,必须成对使用。三目 = 需要三个操作数(表达式1、表达式2、表达式3)。

生活比喻:路口立一块二选一的路牌:"前面堵车吗?走辅路 : 走主路"。看一眼,选一条,一句话说完。

一般形式:

c
表达式1 ? 表达式2 : 表达式3

执行规则(考点):

  1. 先算表达式 1;
  2. 若为非 0(真),整个式子的值 = 表达式 2 的值,表达式 3 不计算;
  3. 若为 0(假),整个式子的值 = 表达式 3 的值,表达式 2 不计算。

所以它也是"短路"的,只算一条分支。

怎么用:

c
max = (a > b) ? a : b;              /* 取两者较大值 */
ch = (ch >= 'A' && ch <= 'Z') ? (ch + 32) : ch;   /* 大写转小写 */
a > b ? printf("%d", a) : printf("%d", b);        /* 当语句用 */

优先级:条件运算符低于关系运算符和算术运算符,高于赋值运算符。所以 max = a > b ? a : b; 不用加括号也对(先算 a>b,再算 ?:,最后赋值)。

结合方向:自右至左(右结合)。所以可以嵌套:

c
max = (a > b) ? a : ((b > c) ? b : c);   /* 三个数最大值 */

易错 ?: 的两个分支类型最好一致;a > b ? a : b 里如果一个是 int 一个是 double,会发生隐式转换,初学阶段别玩这种。

考点 条件表达式可以出现在任何"表达式能出现"的地方;也可以单独成句(后面加分号),但初学阶段用得不多——书上原话"初学时用得不多"。

4.6 选择结构的嵌套

📖 教材 p.96–98 · 正文总结

一句话:if 里面再套 if,就叫 if 的嵌套。

拆名字:nest 英文是"鸟巢",动词是"筑巢、嵌套"。大盒子装小盒子,就是嵌套。

生活比喻:岔路里还有岔路。先下高速(外层 if),下了匝道又分左右(内层 if)。

一般形式:

c
if (表达式1)
    if (表达式2) 语句1
    else 语句2
else
    if (表达式3) 语句3
    else 语句4

考点悬空 else 问题(本章第二大坑):

规则只有一句:else 总是与它上面最近的、还没配对的 if 配对。

c
if (x > 0)
    if (y > 0) printf("A");
else
    printf("B");

很多人看缩进以为 else 配外层 if,错。C 编译器不看缩进,只看距离:else 离内层 if (y > 0) 最近,所以配的是内层。实际含义是:

c
if (x > 0)
{
    if (y > 0) printf("A");
    else       printf("B");
}

怎么避免:书上给了三招——

  1. 让内层 if 也带 else,if 和 else 数量相等,从内到外一一对应;
  2. 用花括号把内层 if 括起来,明确"势力范围";
  3. 写代码一律锯齿形缩进(书上反复强调)。

易错 依赖缩进判断配对关系,是初学者最常见的读程序错误。考试给一段缩进很迷惑的代码问输出,就按"最近未配对"规则数。

4.7 用 switch 语句实现多分支选择结构

📖 教材 p.99–101 · 正文总结

一句话:switch 根据一个整数(或字符)表达式的值,直接跳到对应标号处开始执行。

拆名字:switch 英文是"开关、切换"——像老式电灯的拉线开关,扳到哪一档,就接通哪一路。case 是"情况、案例",这里指"一种情况";default = de(加强)+ fault(缺失)→ "缺省、默认",即"以上情况都不是时";break 是"打断、跳出"。

生活比喻:一排带编号的停车位。你的车号是几,就直接开到几号位——不是从 1 号位挨个问过来。这就是 switch 和 if-else if 最大的区别:switch 是"直接跳",if 链是"挨个问"。

一般形式:

c
switch (表达式)
{
    case 常量1: 语句1; break;
    case 常量2: 语句2; break;
    ...
    case 常量n: 语句n; break;
    default:   语句n+1;
}

书上 8 条说明,浓缩成 6 条必记:

  1. switch 括号里的表达式,值必须是整数类型(包括字符型)——不能是 float/double,不能是字符串。
  2. case 后面必须跟常量(或常量表达式),不能是变量,不能是范围。
  3. default 可以没有;没有且所有 case 都不匹配时,switch 什么都不做,直接往下走。
  4. 各 case 出现次序不影响结果,default 也可以写在最前面。
  5. 每个 case 的常量必须互不相同。
  6. case 只起"标号"作用,不在这里做条件检查。匹配到入口后,就一直往下执行,不再判断——除非遇到 break。

考点贯穿(fall-through):漏写 break,会从匹配的 case 一路往下执行,把后面所有 case 的语句全做一遍。书上例 4.6 若去掉全部 break,输入 A 会连续输出:

c
Your score:85~100
70~84
60~69
<60
enter data error!

易错switch 里最容易错的四件事:

c
switch (score)                    /* 错:case 90 只匹配 90,85 没门 */
{
    case 90: ...                  /* 想分段应该 switch (score / 10) */
}

case 1, 2: ...                    /* 错:多个 case 要写成 */
case 1: case 2: ...               /*     这样,共用一组语句 */

switch (x) { case 1: int y = 0; } /* 错:C 里 case 标号后直接定义变量有坑 */

case 'A': printf("A");            /* 漏 break:会连带执行后面的 case */

考点多个 case 共用一组语句的写法(书上例 4.10 用过):

c
case 2:
case 3: d = 5; break;    /* c 等于 2 或 3,都执行 d=5 */

4.8 选择结构程序综合举例

📖 教材 p.102–106 · 正文总结

一句话:把 if、嵌套、switch、逻辑表达式混在一起,解决三个真实问题——闰年、一元二次方程、分段计费。

书上三个例子各有代表:

  • 例 4.8 闰年:同一个问题给了 3 种写法(三层嵌套 if / else if 链 / 一个逻辑表达式)。这是"一题多解"的活教材,也是本章最可能考的程序。
  • 例 4.9 一元二次方程:考"边界情况想全"——a=0 不是二次方程、disc=0、disc>0、disc<0 四种情况,一种都不能漏;还教你浮点数判等的正确姿势 fabs(disc) <= 1e-6。
  • 例 4.10 运费:考"找规律"。折扣变化点都是 250 的倍数,于是令 c = s / 250,把 5 段折扣压成几个 case,用 switch 一次解决。

三个例子的完整代码和逐行解释,见下一节。

书末习题(书页 107–109)

📖 教材 p.107–109 · 习题

本章书末共 12 题:第 1、2 题概念,第 3 题算逻辑表达式,第 4–6 题编程,第 7 题读程序找错,第 8–12 题编程。下一节逐题精讲,一题不漏。


三、现场写代码

📖 教材例题 · 本课范围 p.83–106

例 4.1 解 ax²+bx+c=0(只处理实根)

📖 教材 p.84 · 例题 4.1

题目:由键盘输入 a、b、c,值任意,不保证 b²−4ac≥0。若 ≥0 就算并输出两个实根,否则输出"此方程无实根"。

思路:先算判别式 disc = b²−4ac,用 if 分两路:disc<0 打印无实根;否则算 p=−b/2a、q=√disc/2a,x1=p+q、x2=p−q。

c
#include <stdio.h>
#include <math.h>
int main()
{
    double a, b, c, disc, x1, x2, p, q;
    scanf("%lf%lf%lf", &a, &b, &c);
    disc = b * b - 4 * a * c;
    if (disc < 0)
        printf("This equation hasn't real roots\n");
    else
    {
        p = -b / (2.0 * a);
        q = sqrt(disc) / (2.0 * a);
        x1 = p + q;
        x2 = p - q;
        printf("real roots:\nx1=%7.2f\nx2=%7.2f\n", x1, x2);
    }
    return 0;
}

逐行解释:

  • 第 1–3 行:stdio.h 管输入输出,math.h 管 sqrt,main 是程序入口。
  • 第 4 行:全部用 double——书上的理由是提高精度、避免编译警告。
  • 第 5 行:%lf 读双精度数,l 是 long(长浮点型)。
  • 第 6 行:先算判别式存进 disc,后面反复用,不重复计算。
  • 第 7–8 行:disc<0 这一支,打印"无实根"。
  • 第 9–10 行:else 后面有 6 条语句,是复合语句,必须用花括号括起来。
  • 第 11–14 行:p 是两根中点,q 是半距离,x1=p+q、x2=p-q。
  • 第 15 行:%7.2f = 总宽 7 位、保留两位小数。
  • 第 16–17 行:复合语句结束,return 0 收尾。

运行结果(书上跑了两次):

c
6 3 1
This equation hasn't real roots
c
2 4 1
real roots:
x1=  -0.29
x2=  -1.71

易错 用 %f 读 double 会读错!scanf 读 double 必须用 %lf;printf 输出 double 用 %f 或 %lf 都行(printf 里 double 自动提升)。

例 4.2 两个实数按从小到大输出

📖 教材 p.85 · 例题 4.2

题目:输入两个实数,按由小到大的顺序输出。 思路:比较一次;如果 a>b,就交换 a、b。交换必须借第三个变量 t。

c
#include <stdio.h>
int main()
{
    float a, b, t;
    scanf("%f,%f", &a, &b);
    if (a > b)
    {
        t = a;
        a = b;
        b = t;
    }
    printf("%5.2f,%5.2f\n", a, b);
    return 0;
}

逐行解释:

  • 第 1–3 行:头文件、入口、float 三个变量(t 是交换用的"空杯子")。
  • 第 4 行:格式串里有个逗号,输入时也要敲逗号,如 3.6,-3.2。
  • 第 5–6 行:前大后小就进 if;交换三句是一个整体,用花括号括起来。
  • 第 7–9 行:t=a 暂存,a=b 覆盖,b=t 还原——三只杯子法。
  • 第 10–11 行:复合语句结束;此时 a 小 b 大,按序输出,%5.2f 宽 5 位两位小数。
  • 第 12 行:结束。

运行结果:

c
3.6,-3.2
-3.20, 3.60

例 4.3 三个数按从小到大输出

📖 教材 p.86 · 例题 4.3

题目:输入 a、b、c,按由小到大输出。 思路(书上伪代码):S1 若 a>b 交换;S2 若 a>c 交换;S3 若 b>c 交换;S4 顺序输出。三次比较下来,a 最小、b 次小、c 最大。

c
#include <stdio.h>
int main()
{
    float a, b, c, t;
    scanf("%f,%f,%f", &a, &b, &c);
    if (a > b)
    { t = a; a = b; b = t; }
    if (a > c)
    { t = a; a = c; c = t; }
    if (b > c)
    { t = b; b = c; c = t; }
    printf("%5.2f,%5.2f,%5.2f\n", a, b, c);
    return 0;
}

逐行解释:

  • 第 1–3 行:头文件、入口、三个数 + 一个临时杯。
  • 第 4 行:三个数之间用逗号隔开输入。
  • 第 5–7 行:三次交换——先保证 a≤b,再保证 a≤c(此时 a 已是三者最小),最后保证 b≤c。
  • 第 8–10 行:此时整体有序,按序输出后结束。

运行结果(输入 3,7,1):

c
3,7,1
 1.00, 3.00, 7.00

例 4.4 大写字母转小写(条件运算符)

📖 教材 p.96 · 例题 4.4

题目:输入一个字符,是大写字母就转成小写,不是就不转,最后输出。 思路:用条件表达式,一句话完成"判断 + 二选一"。

c
#include <stdio.h>
int main()
{
    char ch;
    scanf("%c", &ch);
    ch = (ch >= 'A' && ch <= 'Z') ? (ch + 32) : ch;
    printf("%c\n", ch);
    return 0;
}

逐行解释:

  • 第 1–4 行:头文件、入口、字符变量、读一个字符(空格和回车也会被读进来)。
  • 第 5 行:条件表达式——ch>='A' && ch<='Z' 判断大写;是就取 ch+32(ASCII 里小写比大写大 32),不是就取原值。
  • 第 6–7 行:按字符输出,结束。

运行结果:

c
A
a

例 4.5 阶跃函数(嵌套 if 的两种写法)

📖 教材 p.97 · 例题 4.5

题目:y = −1 (x<0),y = 0 (x=0),y = 1 (x>0)。输入 x,输出 y。 思路:三个值,必须用嵌套 if。书上给两种写法,第二种故意把 if 写在外层——正是"悬空 else"的实战。

程序 1(推荐写法:内层 if 放在外层 else 里):

c
#include <stdio.h>
int main()
{
    int x, y;
    scanf("%d", &x);
    if (x < 0)
        y = -1;
    else if (x == 0)
        y = 0;
    else
        y = 1;
    printf("x=%d,y=%d\n", x, y);
    return 0;
}

逐行解释:

  • 第 1–4 行:头文件、入口、两个整型变量、读入 x。
  • 第 5–10 行:三岔口——x<0 取 −1;x==0 取 0;剩下的必是 x>0,取 1。注意这里用的是 ==。
  • 第 11–12 行:输出 x、y,结束。

程序 2(书上"注意分析此 if 语句"的写法):

c
#include <stdio.h>
int main()
{
    int x, y;
    scanf("%d", &x);
    if (x >= 0)
        if (x > 0) y = 1;
        else       y = 0;
    else
        y = -1;
    printf("x=%d,y=%d\n", x, y);
    return 0;
}

逐行解释:

  • 第 1–4 行:同程序 1,读入 x。
  • 第 5–6 行:外层 if (x >= 0);内层 if (x > 0) y = 1;。
  • 第 7 行:这个 else 配的是内层 if(最近的未配对 if),处理 x==0。
  • 第 8 行:这个 else 配的是外层 if,处理 x<0。
  • 第 9–10 行:输出,结束。

运行结果:输入 -5 输出 x=-5,y=-1;输入 5 输出 x=5,y=1。

例 4.6 按成绩等级输出分数段(switch 入门)

📖 教材 p.99 · 例题 4.6

题目:A 等 85 分以上,B 等 70~84,C 等 60~69,D 等 60 以下。等级由键盘输入。

c
#include <stdio.h>
int main()
{
    char grade;
    scanf("%c", &grade);
    printf("Your score:");
    switch (grade)
    {
        case 'A': printf("85~100\n"); break;
        case 'B': printf("70~84\n");  break;
        case 'C': printf("60~69\n");  break;
        case 'D': printf("<60\n");    break;
        default:  printf("enter data error!\n");
    }
    return 0;
}

逐行解释:

  • 第 1–5 行:头文件、入口、字符变量、读字符、打印提示前缀(不换行)。
  • 第 6 行:switch (grade)——看 grade 是哪个"车位号"。
  • 第 7–9 行:每个 case 匹配一种等级,打印分数段后立即 break 跳出。
  • 第 10–11 行:default 兜底(比如输入 E 或小写 a),打印错误提示;它是最后一句,可不加 break。
  • 第 12 行:结束。

运行结果:

c
A
Your score:85~100

例 4.7 用 switch 处理菜单命令

📖 教材 p.101 · 例题 4.7

题目:输入 'A'/'a' 执行 A 操作,输入 'B'/'b' 执行 B 操作,其他字符报警。 说明:书上原版把两个操作写成 action1、action2 两个函数(第 7 章才讲)。这里换成 printf 的等价版本,先专注 switch 的"多个 case 共用一组语句"。

c
#include <stdio.h>
int main()
{
    char ch;
    int a = 15, b = 23;
    ch = getchar();
    switch (ch)
    {
        case 'a':
        case 'A': printf("x+y=%d\n", a + b); break;
        case 'b':
        case 'B': printf("x*y=%d\n", a * b); break;
        default:  putchar('\a');
    }
    return 0;
}

逐行解释:

  • 第 1–5 行:头文件、入口、命令字符 ch、两个操作数 a=15、b=23,getchar() 读一个字符。
  • 第 6 行:switch (ch) 按命令字符跳转。
  • 第 7–9 行:case 'a': 空着不写语句,直接落到 case 'A':——两个 case 共用一组语句;'b'/'B' 同理。
  • 第 10–11 行:default 用 putchar('\a') 响铃警告(\a 是响铃符),结束。

例 4.8 判断闰年(一题三解)

📖 教材 p.102 · 例题 4.8

题目:判断某一年是否为闰年。 思路:闰年 = 能被 4 整除且不能被 100 整除,或能被 400 整除。

程序 1(三层嵌套 if,书上原版):

c
#include <stdio.h>
int main()
{
    int year, leap;
    printf("enter year:");
    scanf("%d", &year);
    if (year % 4 == 0)
    {
        if (year % 100 == 0)
        {
            if (year % 400 == 0)
                leap = 1;
            else
                leap = 0;
        }
        else
            leap = 1;
    }
    else
        leap = 0;
    if (leap)
        printf("%d is ", year);
    else
        printf("%d is not ", year);
    printf("a leap year.\n");
    return 0;
}

逐行解释:

  • 第 1–5 行:头文件、入口、year 存年份、leap 是"标志变量"(1 闰年 / 0 非闰年),提示并读入。
  • 第 6–15 行:三层嵌套——能被 4 整除?能被 100 整除?能被 400 整除?只有"能被 4 整除且不能被 100 整除"或"能被 400 整除"才 leap=1。
  • 第 16–19 行:if (leap) 等价于 if (leap != 0),按标志打印 "xxxx is " 或 "xxxx is not "。
  • 第 20 行:补上 "a leap year.";这句在 if-else 外面,两种情况都会执行。
  • 第 21 行:结束。

程序 2(else if 链,书上改法):

c
    if (year % 4 != 0)
        leap = 0;
    else if (year % 100 != 0)
        leap = 1;
    else if (year % 400 != 0)
        leap = 0;
    else
        leap = 1;

程序 3(一个逻辑表达式,最简洁):

c
    if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
        leap = 1;
    else
        leap = 0;

运行结果:输入 2012 输出 2012 is a leap year.;输入 2100 输出 2100 is not a leap year.

例 4.9 一元二次方程(四种情况全覆盖)

📖 教材 p.104 · 例题 4.9

题目:求 ax²+bx+c=0 的解。要考虑:a=0 不是二次方程;disc=0 两个相等实根;disc>0 两个不等实根;disc<0 两个共轭复根。

c
#include <stdio.h>
#include <math.h>
int main()
{
    double a, b, c, disc, x1, x2, realpart, imagpart;
    scanf("%lf,%lf,%lf", &a, &b, &c);
    printf("The equation ");
    if (fabs(a) <= 1e-6)
        printf("is not a quadratic\n");
    else
    {
        disc = b * b - 4 * a * c;
        if (fabs(disc) <= 1e-6)
            printf("has two equal roots:%8.4f\n", -b / (2 * a));
        else if (disc > 1e-6)
        {
            x1 = (-b + sqrt(disc)) / (2 * a);
            x2 = (-b - sqrt(disc)) / (2 * a);
            printf("has distinct real roots:%8.4f and %8.4f\n", x1, x2);
        }
        else
        {
            realpart = -b / (2 * a);
            imagpart = sqrt(-disc) / (2 * a);
            printf("has complex roots:\n");
            printf("%8.4f+%8.4fi\n", realpart, imagpart);
            printf("%8.4f-%8.4fi\n", realpart, imagpart);
        }
    }
    return 0;
}

逐行解释:

  • 第 1–5 行:头文件(math.h 提供 fabs、sqrt)、入口、变量、逗号分隔读入(如 1,2,1)。
  • 第 6 行:先输出半句 "The equation ",不换行。
  • 第 7–9 行:fabs(a)<=1e-6 判断 a 是否近似为 0——不能写 a==0;是则不是二次方程。
  • 第 10 行:算判别式 disc。
  • 第 11–12 行:disc 近似为 0 → 两个相等实根。
  • 第 13–15 行:disc 明显大于 0 → 两个不等实根,%8.4f 共 8 位、4 位小数。
  • 第 16–19 行:剩下 disc<0 → 实部 -b/(2a)、虚部 sqrt(-disc)/(2a)(注意里面是 −disc),再打印 p+qi 和 p−qi。
  • 第 20 行:结束。

运行结果(书上跑三次):1,2,1 → two equal roots: -1.0000;1,2,2 → 两个复根 -1.0000+1.0000i / -1.0000-1.0000i;2,6,1 → distinct real roots: -0.1771 and -2.8229。

例 4.10 按距离计算运费(switch 找规律)

📖 教材 p.105 · 例题 4.10

题目:s<250 无折扣;250≤s<500 打 2%;500≤s<1000 打 5%;1000≤s<2000 打 8%;2000≤s<3000 打 10%;s≥3000 打 15%。公式 f = p×w×s×(1−d/100)。

思路:折扣变化点都是 250 的倍数,令 c = s/250(整数除法),c=0 是 250 以下,c=1 是 250~500,c=2,3 是 500~1000,c=4~7 是 1000~2000,c=8~11 是 2000~3000,c≥12 是 3000 以上。

c
#include <stdio.h>
int main()
{
    int c, s;
    float p, w, d, f;
    printf("please enter price,weight,distance:");
    scanf("%f,%f,%d", &p, &w, &s);
    if (s >= 3000)
        c = 12;
    else
        c = s / 250;
    switch (c)
    {
        case 0:  d = 0;  break;
        case 1:  d = 2;  break;
        case 2:
        case 3:  d = 5;  break;
        case 4:
        case 5:
        case 6:
        case 7:  d = 8;  break;
        case 8:
        case 9:
        case 10:
        case 11: d = 10; break;
        default: d = 15;
    }
    f = p * w * s * (1 - d / 100);
    printf("freight=%10.2f\n", f);
    return 0;
}

逐行解释:

  • 第 1–5 行:头文件、入口、整型 c 和 s、浮点 p/w/d/f、打印提示。
  • 第 6 行:注意第三个是 %d(int),前两个是 %f。
  • 第 7–8 行:3000 以上统一令 c=12(一个 case 就够了);否则 c = s/250,整数除法自动取整,把距离映射成折扣除数。
  • 第 9–14 行:switch 按 c 跳转;case 2: 空着落到 case 3:,表示 2 和 3 共用 d=5;4~7 共用 d=8,8~11 共用 d=10;default 处理 c=12,d=15。
  • 第 15–17 行:套公式 f=p×w×s×(1−d/100),%10.2f 宽 10 位两位小数,结束。

运行结果:100,20,300 → freight= 588000.00。


四、书上习题精讲

📖 教材 p.107–109 · 习题

第 1 题 什么是算术运算?什么是关系运算?什么是逻辑运算?

📖 教材 p.107 · 习题 1

答:

  • 算术运算:对数值做加减乘除求余(+ - * / %),运算对象是数,结果还是数。
  • 关系运算:对两个值做比较(< <= > >= == !=),结果是"真"或"假"(C 里用 1 / 0 表示)。
  • 逻辑运算:对逻辑量做与、或、非(&& || !),结果也是"真"或"假"。

一句话区分:算术运算算"多少",关系运算比"大小",逻辑运算拼"条件"。

第 2 题 C 语言中如何表示"真"和"假"?系统如何判断一个量的"真"和"假"?

📖 教材 p.107 · 习题 2

答:

  • 表示:1 代表真,0 代表假。逻辑运算和关系运算的结果只可能是 1 或 0。
  • 判断:系统判断一个量时,0 为假,非 0 为真。所以 if (x) 里 x=5、x=−3、x=0.1 都算真,x=0 才算假。

注意这两句话不矛盾:"产生的结果"只能是 0/1,"判断的对象"可以是非 0 的任何值。

第 3 题 写出下面各逻辑表达式的值(设 a=3, b=4, c=5)

📖 教材 p.107 · 习题 3
小题表达式计算过程值
(1)a+b>c && b==c3+4=7>5 得 1;4==5 得 0;1&&00
(2)a || b+c && b-c先算 b+c=9、b-c=-1;&& 优先于 ||:9&&−1 得 1;3||11
(3)!(a>b) && !c || 1a>b=1,!1=0;!c=!5=0;0&&0=0;0||11
(4)!(x=a) && (y=b) && 0x=a 得 3,!3=0;&& 短路,后面不算0
(5)!(a+b)+c-1 && b+c/2!(7)=0;0+5−1=4;c/2=2(整数除法),b+2=6;4&&61

考点 第 (2) 题考优先级,第 (4) 题考短路,第 (5) 题考整数除法——三个坑一题一个。

第 4 题 输入 3 个整数,输出其中最大的数

📖 教材 p.107 · 习题 4

思路:先假定第一个是最大值,再拿后两个依次挑战它。

c
#include <stdio.h>
int main()
{
    int a, b, c, max;
    scanf("%d%d%d", &a, &b, &c);
    max = a;
    if (b > max) max = b;
    if (c > max) max = c;
    printf("max=%d\n", max);
    return 0;
}

解析:两个独立的 if(不是 else if)——因为 b、c 都要和当前最大值比。也可以写成 if (a>b) ... else ... 的嵌套版,但"打擂台"写法最不容易错。

第 5 题 输入小于 1000 的正数,输出其平方根(不是整数则取整数部分);先检查输入是否合法

📖 教材 p.107 · 习题 5

思路:先判断 x > 0 && x < 1000,合法才 sqrt,否则提示错误。题目还要求"重新输入",那需要循环——第 5 章才学,这里给出两个版本。

版本 1(本章能写:只检查一次):

c
#include <stdio.h>
#include <math.h>
int main()
{
    double x;
    printf("enter a positive number less than 1000:");
    scanf("%lf", &x);
    if (x > 0 && x < 1000)
        printf("%d\n", (int)sqrt(x));
    else
        printf("input error!\n");
    return 0;
}

解析:

  1. x > 0 && x < 1000 不能写成 0 < x < 1000(恒真)。
  2. (int)sqrt(x) 把 double 强制转成 int,小数部分直接截掉,正好满足"取整数部分"。
  3. 负数和 0 都算非法,所以是 x > 0 而不是 x >= 0。

版本 2(预习第 5 章:用 do-while 反复要求重新输入):

c
#include <stdio.h>
#include <math.h>
int main()
{
    double x;
    do
    {
        printf("enter a positive number less than 1000:");
        scanf("%lf", &x);
    } while (x <= 0 || x >= 1000);
    printf("%d\n", (int)sqrt(x));
    return 0;
}

解析:do-while 是"先做一次,再判断要不要重做",正好符合"输入—检查—不合法就重来"。这里只要看懂结构,第 5 章会细讲。

第 6 题 分段函数求值

📖 教材 p.108 · 习题 6

题目:y = x (x<1);y = 2x−1 (1≤x<10);y = 3x−11 (x≥10)。

c
#include <stdio.h>
int main()
{
    float x, y;
    scanf("%f", &x);
    if (x < 1)
        y = x;
    else if (x < 10)
        y = 2 * x - 1;
    else
        y = 3 * x - 11;
    printf("x=%.2f,y=%.2f\n", x, y);
    return 0;
}

解析:第二个条件只写 x < 10 就够了——能走到这一支,说明前面 x < 1 已经不成立,即 x≥1 自动满足。这就是 else if 链的"前面已排除"效应,不要傻乎乎写成 x >= 1 && x < 10(虽然也对,但啰嗦)。

第 7 题 分析两个程序能否实现阶跃函数

📖 教材 p.108 · 习题 7

题目:给两个程序(书页 108),先分析逻辑、画流程图,再上机验证。函数同例 4.5。

程序(1):

c
    int x, y;
    printf("enter x:");
    scanf("%d", &x);
    y = -1;
    if (x != 0)
        if (x > 0)
            y = 1;
        else
            y = 0;
    printf("x=%d,y=%d\n", x, y);

分析:else 与最近的未配对 if(即 if (x>0))配对,所以逻辑是:

  • x≠0 时:若 x>0 → y=1;否则(x<0)→ y=0(错!应该 y=−1)
  • x=0 时:外层条件不成立,y 保持 −1(错!应该 y=0)
输入正确答案程序输出对错
x=511对
x=00−1错
x=−5−10错

结论:不能实现。错在 else 配错了对象。

程序(2):

c
    int x, y;
    printf("enter x:");
    scanf("%d", &x);
    y = 0;
    if (x >= 0)
        if (x > 0) y = 1;
        else y = -1;
    printf("x=%d,y=%d\n", x, y);

分析:else 配内层 if。

  • x≥0 时:x>0 → 1;x=0 → −1(错,应 0)
  • x<0 时:外层不成立,y 保持 0(错,应 −1)
输入正确答案程序输出对错
x=511对
x=00−1错
x=−5−10错

结论:也不能实现。两个程序都只对 x>0 正确。

正确写法见例 4.5 程序 1(把内层 if 放进外层 else 里,或老老实实写 else if 链)。

第 8 题 百分制成绩转等级 A~E

📖 教材 p.109 · 习题 8

题目:90 以上 A,80~89 B,70~79 C,60~69 D,60 以下 E。

写法 1(else if 链):

c
#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("E\n");
    return 0;
}

写法 2(switch):

c
    switch (score / 10)
    {
        case 10:
        case 9:  printf("A\n"); break;
        case 8:  printf("B\n"); break;
        case 7:  printf("C\n"); break;
        case 6:  printf("D\n"); break;
        default: printf("E\n");
    }

解析:score/10 把百分制压成 0~10 的整数,正好能当 switch 的入口。注意 100 分时 score/10 = 10,必须把 case 10 和 case 9 并列,否则满分会被判成 E。这是本题唯一的坑。

第 9 题 不多于 5 位的正整数:求位数、逐位输出、逆序输出

📖 教材 p.109 · 习题 9

思路:先用 if 链定位数(存进 place),再用 / 和 % 拆出每一位,最后用 switch 按位数输出。

c
#include <stdio.h>
int main()
{
    int num, place, d5, d4, d3, d2, d1;
    scanf("%d", &num);
    if (num > 9999)      place = 5;
    else if (num > 999)  place = 4;
    else if (num > 99)   place = 3;
    else if (num > 9)    place = 2;
    else                 place = 1;
    d5 = num / 10000;
    d4 = num / 1000 % 10;
    d3 = num / 100 % 10;
    d2 = num / 10 % 10;
    d1 = num % 10;
    printf("place=%d\n", place);
    switch (place)
    {
        case 5:  printf("%d %d %d %d %d\n", d5, d4, d3, d2, d1); break;
        case 4:  printf("%d %d %d %d\n", d4, d3, d2, d1); break;
        case 3:  printf("%d %d %d\n", d3, d2, d1); break;
        case 2:  printf("%d %d\n", d2, d1); break;
        case 1:  printf("%d\n", d1); break;
    }
    printf("reverse=");
    switch (place)
    {
        case 5:  printf("%d%d%d%d%d\n", d1, d2, d3, d4, d5); break;
        case 4:  printf("%d%d%d%d\n", d1, d2, d3, d4); break;
        case 3:  printf("%d%d%d\n", d1, d2, d3); break;
        case 2:  printf("%d%d\n", d1, d2); break;
        case 1:  printf("%d\n", d1); break;
    }
    return 0;
}

解析:

  1. 定位数用 > 从大到小问:num>9999 就是 5 位,num>999 就是 4 位……顺序不能乱,否则 12345 会被判成 4 位。
  2. num/1000%10 的意思是"先去掉后三位,再取个位",即千位。这是拆数字的通用套路。
  3. 两个 switch 一个正序、一个逆序,正好各用一次。

运行结果(输入 321):place=3 → 3 2 1 → reverse=123。

第 10 题 按利润提成算奖金

📖 教材 p.109 · 习题 10

题目:I≤10 万提 10%;10 万<I≤20 万,超出 10 万部分提 7.5%;20 万<I≤40 万,超出 20 万部分提 5%;40 万<I≤60 万,超出 40 万部分提 3%;60 万<I≤100 万,超出 60 万部分提 1.5%;I>100 万,超出 100 万部分提 1%。

思路:分段累加。先算好各段"封顶奖金":10 万 → 1 万;20 万 → 1.75 万;40 万 → 2.75 万;60 万 → 3.35 万;100 万 → 3.95 万。

(1)用 if 语句:

c
#include <stdio.h>
int main()
{
    double i, bonus;
    scanf("%lf", &i);
    if (i <= 100000)
        bonus = i * 0.1;
    else if (i <= 200000)
        bonus = 10000 + (i - 100000) * 0.075;
    else if (i <= 400000)
        bonus = 17500 + (i - 200000) * 0.05;
    else if (i <= 600000)
        bonus = 27500 + (i - 400000) * 0.03;
    else if (i <= 1000000)
        bonus = 33500 + (i - 600000) * 0.015;
    else
        bonus = 39500 + (i - 1000000) * 0.01;
    printf("bonus=%10.2f\n", bonus);
    return 0;
}

(2)用 switch 语句:

c
#include <stdio.h>
int main()
{
    double i, bonus;
    int c;
    scanf("%lf", &i);
    c = (int)(i / 100000);
    switch (c)
    {
        case 0:
            bonus = i * 0.1; break;
        case 1:
            bonus = 10000 + (i - 100000) * 0.075; break;
        case 2:
        case 3:
            bonus = 17500 + (i - 200000) * 0.05; break;
        case 4:
        case 5:
            bonus = 27500 + (i - 400000) * 0.03; break;
        case 6:
        case 7:
        case 8:
        case 9:
            bonus = 33500 + (i - 600000) * 0.015; break;
        default:
            bonus = 39500 + (i - 1000000) * 0.01;
    }
    printf("bonus=%10.2f\n", bonus);
    return 0;
}

解析:10000 / 17500 / 27500 / 33500 / 39500 就是"前面各段封顶奖金",思路里已经算好,直接当常数用,代码短很多。(int)(i/100000) 把利润压成"十万"为单位,边界刚好对:i=100000 → 1(与 if 版 i<=100000 结果相同);i=200000 → 2;i=1000000 → 10,走 default。每个 case 的边界都要验一遍,这是分段题最容易翻车的地方。

第 11 题 输入 4 个整数,按从小到大输出

📖 教材 p.109 · 习题 11

思路:和例 4.3 一样,但比较次数从 3 次变成 6 次:a 依次和 b、c、d 比,b 和 c、d 比,c 和 d 比。这叫"选择排序"的雏形。

c
#include <stdio.h>
int main()
{
    int a, b, c, d, t;
    scanf("%d%d%d%d", &a, &b, &c, &d);
    if (a > b) { t = a; a = b; b = t; }
    if (a > c) { t = a; a = c; c = t; }
    if (a > d) { t = a; a = d; d = t; }
    if (b > c) { t = b; b = c; c = t; }
    if (b > d) { t = b; b = d; d = t; }
    if (c > d) { t = c; c = d; d = t; }
    printf("%d %d %d %d\n", a, b, c, d);
    return 0;
}

解析:前 3 个 if 把最小的换到 a;中间 2 个把次小的换到 b;最后 1 个把 c、d 排好。6 次比较一次不多一次不少——这是 4 个数排序的最少比较次数。

第 12 题 圆塔高度

📖 教材 p.109 · 习题 12

题目:4 个圆塔,圆心 (2,2)、(−2,2)、(−2,−2)、(2,−2),半径 1,高 10 m,塔外高度为 0。输入任一点坐标,求该点建筑高度。

思路:点到圆心的距离 ≤ 半径,就在圆内。用"平方比较"避免开方:(x−cx)²+(y−cy)² ≤ 1。四个圆心共用一套公式,用 || 连起来。

c
#include <stdio.h>
int main()
{
    double x, y;
    printf("enter x,y:");
    scanf("%lf,%lf", &x, &y);
    if ((x - 2) * (x - 2) + (y - 2) * (y - 2) <= 1
        || (x + 2) * (x + 2) + (y - 2) * (y - 2) <= 1
        || (x + 2) * (x + 2) + (y + 2) * (y + 2) <= 1
        || (x - 2) * (x - 2) + (y + 2) * (y + 2) <= 1)
        printf("height=10\n");
    else
        printf("height=0\n");
    return 0;
}

解析:

  1. 圆心 (−2,2) 对应 (x+2)²+(y−2)²——减负等于加。
  2. 用平方和 ≤ 1(即 r²)比开方更快、更准,还不用 math.h。
  3. 四个条件用 || 连接:落在任意一个塔里都算 10 m。
  4. && 与 || 混用时一定加括号,这里每个距离都用括号包住了。

🛠 随课练习(只给题不给答案)

📖 本课补充 · 非教材原文

🔮 猜输出

猜输出 1

c
#include <stdio.h>
int main()
{
    int x = 5;
    if (x > 3)
        printf("A");
        printf("B");
    return 0;
}

猜输出 2

c
#include <stdio.h>
int main()
{
    int x = 0, y = 1;
    if (x == 0)
        if (y == 0) printf("A");
        else        printf("B");
    return 0;
}

猜输出 3

c
#include <stdio.h>
int main()
{
    int a = 1, b = 2, c = 3, d = 4, m = 1, n = 1;
    (m = a > b) && (n = c > d);
    printf("%d %d\n", m, n);
    return 0;
}

猜输出 4

c
#include <stdio.h>
int main()
{
    int n = 2;
    switch (n)
    {
        case 1: printf("1");
        case 2: printf("2");
        case 3: printf("3"); break;
        default: printf("4");
    }
    return 0;
}

🛠 变式题

变式 1(照猫画虎):模仿例 4.2,输入两个整数,按从大到小输出。

变式 2(改需求):仿照例 4.6 的 switch 写法,写一个"分数 → 等级"的五档判断:90 及以上 A、80~89 B、70~79 C、60~69 D、60 以下 E。要求用 switch 写,而不是 if 链。

变式 3(翻译):输入 1~7 的整数,输出对应的星期英文名(1→Monday … 7→Sunday),其他数字输出 error。用 switch 写。

变式 4(补全):判断一个整数能否同时被 3 和 5 整除,请补全下面这个 if 的条件(假设 n 已经读入)。

c
if (____ && ____)          /* 两个空都要填:一个管 3,一个管 5 */
    printf("yes\n");
else
    printf("no\n");

🐛 改错题

改错 1

c
#include <stdio.h>
int main()
{
    int x = 10;
    if (x > 0);
        printf("positive\n");
    return 0;
}

改错 2

c
#include <stdio.h>
int main()
{
    int a = 0;
    if (a = 1)
        printf("equal\n");
    else
        printf("not equal\n");
    return 0;
}

改错 3

c
#include <stdio.h>
int main()
{
    int score = 85;
    switch (score)
    {
        case 90: printf("A\n"); break;
        case 80: printf("B\n"); break;
        default: printf("other\n");
    }
    return 0;
}

(提示:程序能编译、能运行,但 85 分输出的是 other。想想 case 到底在比什么。)


📈 加练题(你随时喊,我随时追加到这里)

📖 本课补充 · 非教材原文

(暂无。想加练就说"再出几道 XX 题",我出的题会标上日期追加在这块。)

💬 问答区(你问过的问题 + 我的解答)

📖 本课补充 · 非教材原文

(暂无。你问的问题和我的解答会按 Q/A 整理进这里。)

该带走的 6 句话

📖 本课补充 · 非教材原文
  1. if 括号里是路牌:算出来非 0 就进,等于 0 就跳过;括号里可以放任何数值表达式,不一定是比较。
  2. == 是比较,= 是赋值;判断相等时把常量写左边(5 == a),漏一个等号就会当场编译报错。
  3. else 永远跟最近的、还没配对的 if 走,编译器不看你缩进——拿不准就加花括号。
  4. && 左边为假、|| 左边为真时,右边根本不计算(短路),所以 (m=a>b)&&(n=c>d) 里 n 可能一点没变。
  5. switch 只认整型和字符型表达式,case 后必须是常量;匹配到入口后不刹车就一路贯穿,所以每个 case 后面记得 break。
  6. 三种 if 形式 + switch 能表达一切选择;分段题先把边界一个个验一遍,浮点数判等要用 fabs(x) < 1e-6。

📚 教材覆盖清单(第4章,选择结构程序设计)

这张表把本课和教材逐节对上:从网页可以回查书页,从书页也能找到网页位置。标「未展开」的,就是教材里有、本课没讲的部分,已明确列出。

教材节号标题书页网站对应状态
4.1选择结构和条件判断p.83–844.1 选择结构和条件判断精讲
4.2用if语句实现选择结构p.85–884.2 用 if 语句实现选择结构精讲
4.2.1用if语句处理选择结构举例p.85–864.2.1 用 if 语句处理选择结构举例精讲
4.2.2if语句的一般形式p.87–884.2.2 if 语句的一般形式精讲
4.3关系运算符和关系表达式p.894.3 关系运算符和关系表达式精讲
4.3.1关系运算符及其优先次序p.904.3.1 关系运算符及其优先次序精讲
4.3.2关系表达式p.904.3.2 关系表达式精讲
4.4逻辑运算符和逻辑表达式p.90–934.4 逻辑运算符和逻辑表达式精讲
4.4.1逻辑运算符及其优先次序p.914.4.1 逻辑运算符及其优先次序精讲
4.4.2逻辑表达式p.92–934.4.2 逻辑表达式精讲
4.5条件运算符和条件表达式p.94–954.5 条件运算符和条件表达式精讲
4.6选择结构的嵌套p.96–984.6 选择结构的嵌套精讲
4.7用switch语句实现多分支选择结构p.99–1014.7 用 switch 语句实现多分支选择结构精讲
4.8选择结构程序综合举例p.102–1064.8 选择结构程序综合举例精讲
习题第4章书末习题p.107–109本课「书上习题精讲」逐题精讲(第1–12题)
插图本课范围内教材插图p.83–106用文字/ASCII 重画未逐张复刻
附录附录 A–Ep.357–370全书对照页未单独成课
  • 教材插图(流程图、内存示意图等)本课用文字/ASCII 重画,未逐张复刻,页码见上表。
  • 附录 A–E(p.357–370)是速查表,未单独成课;全站对照页列出。
  • 标「本课补充」的小节(先补地基、比喻、随课练习等)不是教材原文,是为理解而加的。