第六章 选择语句《C语言程序设计现代方法(第2版)》读书笔记
目录
第六章 循环 🚀
6.2 do 语句 🚀
6.3 for 语句 🚀
6.3.1 for 语句的惯用法 🚀
6.3.2 在 for 语句中省略表达式 🚀
6.3.3 C99 中的 for 语句 🚀
6.3.4 逗号运算符 🚀
6.4 退出循环 🚀
6.4.1 break 语句 🚀
6.4.2 continue 语句 🚀
6.4.3 goto 语句 🚀
6.5 空语句 🚀
问与答 🚀
从今天开始,各书的读书笔记就陆陆续续开展了哈(本书为《C语言程序设计现代方法(第二版)》)(一本书一个专栏,订阅会第一时间推荐更新哈),主要会把作者认为比较重要或者比较新奇的知识点记录下来。但是要想真的了解一本书,自己去看可能才会有更深的体会哈。
C语言的入门篇进阶篇和深剖篇都整理在这里了哈。然后这里是个人主页,比点头像更好找文章哈。
作者新建立的社区:非科班转码社区-CSDN社区云💖💛💙
期待hxd的支持哈🎉 🎉 🎉
最后是打鸡血环节:改变的确很难,但结果值得冒险,拿出点勇气来。路还很长,现在才刚开始而已。过去无可挽回,未来可以改变。🚀 🚀 🚀
第六章 循环 🚀
循环 ( loop ) 是重复执行其他语句(循环体)的一种语句。在 C 语言中,每个循环都有一 个 控制表达式 ( controlling expression ) 。每次执行循环体(循环 重复 一次)时都要对控 制表达式求值。如果表达式为真(即值不为零),那么继续执行循环。C 语言提供了 3 种重复语句,即 while 语句、 do 语句和 for 语句。 break 语句用来跳出循环并把程序控制传递到循环后的下一条语句, continue语句用来跳过本次循环的剩余部分,而goto 语句则可以跳到函数内的任何语句上。
6.2 do 语句 🚀
do 语句和 while 语句关系紧密。事实上, do 语句本质上就是 while 语句,只不过其控制表达 式是在每次执行完循环体 之后 进行判定的。 顺便提一下,无论需要与否,最好给所有的 do 语句都加上花括号,这是因为没有花括号的do语句很容易被误认为是 while 语句:
6.3 for 语句 🚀
for 语句和 while 语句关系紧密。事实上,除了一些极少数的情况以外,for 循环总可 以用等价的 while 循环替换: 就像这个模式显示的那样, 表达式 1 是循环开始执行前的初始化步骤,只执行一次; 表达式 2 用来控制循环的终止(只要 表达式 2 的值 不为零,循环持续执行);而 表达式 3 是每次循环中最后被执行的一个操作。把这种模式用于先前的for 循环示例中,可以得到:
研究等价的 while 语句有助于更好地理解 for 语句。例如,假设把先前的 for 循环示例中的 i-- 用 --i 来替换: 这样做会对循环产生什么样的影响呢?看看等价的 while 循环就会发现,这种做法对循环没有 任何影响:
因为for语句中第一个表达式和第三个表达式都是以语句的方式执行的,所以它们的值互不相 关——它们有用仅仅是因为有副作用。 结果是,这两个表达式常常作为赋值表达式或自增 / 自减表达式。
6.3.1 for 语句的惯用法 🚀
6.3.2 在 for 语句中省略表达式 🚀
for 语句远比目前看到的更加灵活。通常 for 语句用三个表达式控制循环,但是有一些 for 循环可能不需要这么多,因此 C语言允许省略任意或全部的表达式。
如果省略第一个表达式,那么在执行循环前没有初始化的操作:
在这个例子中,变量 i 由一条单独的赋值语句实现了初始化,所以在 for 语句中省略了第一个表 达式。(注意,保留第一个表达式和第二个表达式之间的分号。即使省略掉某些表达式,控制表达式也必须始终有两个分号。)如果省略了for语句中的第三个 表达式,循环体需要确保第二个表达式的值最终会变为假。 我们的 for语句示例可以这样写: 为了补偿省略第三个表达式产生的后果,我们使变量 i在循环体中进行自减。 当for 语句同时省略掉第一个和第三个 表达式时,它和while语句没有任何分别。例如,循环 这里 while 语句的形式更清楚,也因此更可取。 如果省略第二个表达式,那么它默认为真值,因此for语句不会终止(除非以某种其他形 式停止)。 例如,某些程序员用下列的for 语句建立无限循环:
6.3.3 C99 中的 for 语句 🚀
在C99 中,for 语句的第一个表达式可以替换为一个声明,这一特性使得程序员可以声明一个用于循环的变量:
变量i不需要在该语句前进行声明。事实上,如果变量i在之前已经进行了声明,这个语句将创 建一个新的i且该值仅用于循环内。 for语句声明的变量不可以在循环外访问(在循环外不 可见):
让for 语句声明自己的循环控制变量通常是一个好办法:这样很方便且程序的可读性更强,但是如果在for 循环退出之后还要使用该变量,则只能使用以前的 for 语句格式。 顺便提一下,for语句可以声明多个变量,只要它们的类型相同:
6.3.4 逗号运算符 🚀
有些时候,我们可能喜欢编写有两个(或更多个)初始表达式的 for 语句,或者希望在每 次循环时一次对几个变量进行自增操作。使用 逗号表达式 ( comma expression )作为 for 语句中第一个或第三个表达式可以实现这些想法。 这里的 表达式 1 和 表达式 2 是两个任意的表达式。逗号表达式的计算要通过两步来实现:第 一步,计算 表达式1并且扔掉计算出的值。第二步,计算表达式2,把这个值作为整个表达式的值。对表达式1的计算应该始终会有副作用;如果没有,那么表达式1就没有了存在的意义。 例如,假设变量 i 和变量 j 的值分别为 1 和 5 ,当计算逗号表达式 ++i , i+j 时,变量 i 先进行自增,然后计算i+j ,所以表达式的值为 7 。(而且,显然现在变量 i 的值为 2 。)顺便说一句,逗号运算符的优先级低于所有其他运算符,所以不需要在++i 和i+j外面加圆括号。 有时需要把一串逗号表达式串联在一起,就如同某些时候把赋值表达式串联在一起一样。逗号运算符是左结合的,所以编译器把表达式 因为逗号表达式的左操作数在右操作数之前求值,所以赋值运算 i = 1 、 j = 2 和 k = i + j是从左向右进行的。 提供逗号运算符是为了在C 语言要求只能有一个表达式的情况下可以使用两个或多个表达式。换句话说,逗号运算符允许将两个表达式“粘贴”在一起构成一个表达式。(注意与复合语句的相似之处,后者允许我们把一组语句当作一条语句来使用。) 需要把多个表达式粘在一起的情况不是很多。正如后面的某一章将介绍的那样,某些宏定义( 14.3 节)可以从逗号运算符中受益。除此之外, for 语句是唯一可以发现逗号运算符的地方。例如,假设在进入for 语句时希望初始化两个变量。可以把原来的程序 表达式sum = 0, i = 1 首先把 0 赋值给 sum ,然后把 1 赋值给 i 。利用附加的逗号运算符, for语句可以初始化更多的变量。
6.4 退出循环 🚀
6.4.1 break 语句 🚀
当这些语句出现嵌套时, break 语句只能跳出一层嵌套。 break 语句可以把程序控制从 switch 语句中转移出来,但是却不能跳出 while 循环。后面会继 续讨论这一点。
6.4.2 continue 语句 🚀
用break语句会使程序控制跳出循环,而continue语句会把程序控制留在循环内。break语句和continue语句的另外一个区别是:break语句可以用于switch语句和循环(while、do和for),而continue语句只能用于循环。
6.4.3 goto 语句 🚀
break 语句和 continue 语句都是跳转语句:它们把控制从程序中的一个位置转移到另一个 位置。这两者都是受限制的: break 语句的目标是包含该语句的循环结束 之后 的那一点,而 continue 语句的目标是循环结束 之前 的那一点。 goto 语句则可以跳转到函数中 任何 有 标号的语句处。(C99增加了一条限制:goto语句不可以用于绕过变长数组( 8.3节)的声明。)
执行语句goto L;,控制会转移到标号L后面的语句上,而且该语句必须和goto语句在同一个函数中。
如果C语言没有break语句,可以用下面的goto语句提前退出循环:
goto 语句在早期编程语言中很常见,但在日常 C 语言编程中却很少用到它了。 break 、continue、 return 语句(本质上都是受限制的 goto 语句)和 exit 函数( 9.5 节)足以应付在 其他编程语言中需要 goto 语句的大多数情况。虽然如此, goto 语句偶尔还是很有用的。考虑从包含 switch 语句的循环中退出的问题。正 如前面看到的那样, break 语句不会产生期望的效果:它可以跳出 switch 语句,但是无法跳出 循环。 goto 语句解决了这个问题: goto 语句对于嵌套循环的退出也是很有用的。
注意, return 语句后面没有 break 语句。紧跟在 return 语句后的 break 语句永远不会执行, 许多编译器还将显示警告消息。
6.5 空语句 🚀
语句可以为 空 ,也就是除了末尾处的分号以外什么符号也没有。 空语句主要有一个好处:编写空循环体的循环。 每次执行循环时,先判定条件 d < n 。如果结果为假,循环终止;否则,判定条件 n % d ! = 0 ,如果结果为假则终止循环。(在后一种情况下,n % d == 0 一定为真;换句话说,找到了 n 的一个约数。)
注意上面是如何把空语句单独放置在一行的,不要写成
C程序员习惯性地把空语句单独放置在一行。否则,一些人阅读程序时可能会混淆for语句 后边的语句是否是其循环体: 把普通循环转化成带空循环体的循环不会带来很大的好处:新循环往往更简洁,但通常不会提高效率。但是在一些情况下,带空循环体的循环比其他循环更高效。例如,这些带空循环体的循环更便于读取字符( 7.3 节)数据。
如果不小心在 if 、 while 或 for 语句的圆括号后放置分号会创建空语句,从而造成 if 、 while 或 for 语句提前结束。1. if 语句中,如果在圆括号后边放置分号,无论控制表达式的值是什么, if 语句执行的动作显然都是一样的:
因为 printf 函数调用不在 if 语句内,所以无论 d 的值是否等于 0 ,都会执行此 函数调用。
2. while语句中,如果在圆括号后边放置分号,会产生无限循环:
另一种可能是循环终止,但是在循环终止后只执行一次循环体语句。
这个例子显示如下消息:
3. for语句中,在圆括号后边放置分号会导致只执行一次循环体语句:
这个例子也显示出如下消息:
问与答 🚀
问: 6.1 节有如下循环:
这种写法的循环会在i达到0值时停止,所以它应该和原始版本一样好。(p.70)
答:新写法确实更加简洁,许多 C 程序员也都这样写循环。但是,它也有缺点。 首先,新循环不像原始版本那样容易阅读。新循环可以清楚地显示出在 i 达到 0 值时循环终止,但是不能清楚地表示是向上计数还是向下计数。而在原始的循环中,根据控制表达式i > 0 可以推断出这一信息。 其次,如果循环开始执行时i 碰巧为负值,那么新循环的行为会不同于原始版本。原始循环会立刻终止,而新循环则不会。
问: 6.3 节提到,大多数 for 循环可以利用标准模式转换成 while 循环。能给出一个反例吗? ( p.74 ) 答:当 for 循环体中含有 continue 语句时, 6.3 节给出的 while 模式将不再有效。思考下面这个来自 6.4节的示例: 但是,这个循环并不等价于原始循环。当 i 等于 0 时,原始循环并没有对 n 进行自增操作,但是新循环却做了。
问:哪个无限循环格式更可取,while(1)还是for(;;)?(p.75)
答: C 程序员传统上喜欢 for(;;) 的高效性;因为早期的编译器经常强制程序在每次执行 while 循环体时测试条件1 。但是,对于现代编译器来说,在性能上两种无限循环应该没有差别。
问:听说程序员应该永不使用continue语句。这种说法对吗?
答:continue语句的确很少使用。尽管如此,continue语句有时还是非常方便的。假设我们编写的循 环要读入一些输入数据并测试其有效性,如果有效则以某种方法进行处理。如果有许多有效性测试,或者如果它们都很复杂,那么continue语句就非常有用了。循环将类似于下面这样:
问:goto语句有什么不好?(p.79)
答: goto 语句不是天生的魔鬼,只是通常它有更好的替代方式。使用过多 goto 语句的程序会迅速退化成“垃圾代码”,因为控制可以随意地跳来跳去。垃圾代码是非常难于理解和修改的。 由于goto语句既可以往前跳又可以往后跳,所以使得程序难于阅读。(break语句和 continue语句只是往前跳。)含有goto 语句的程序经常要求阅读者来回跳转以理解代码的控制流。goto语句使程序难于修改,因为它可能会使某段代码用于多种不同的目的。例如,对于前面有标号的语句,既可以在执行完其前一条语句后到达,也可以通过多条goto 语句中的一条到达。
问:除了说明循环体为空外,空语句还有其他用途吗?(p.82)
答:非常少。空语句可以放在任何允许放语句的地方,所以有许多 潜在 的用途。但在实际中,空语句只有一种别的用途,而且极少使用。 假设需要在复合语句的末尾放置标号。标号不能独立存在,它后面必须有语句。在标号后放置空语句就可以解决这个问题:
问:除了把空语句单独放置在一行以外,是否还有其他方法可以凸显出空循环体?(p.82)
最后的最后,创作不易,希望读者三连支持💖
赠人玫瑰,手有余香💖