> 文档中心 > 第五章 选择语句《C语言程序设计现代方法(第2版)》读书笔记

第五章 选择语句《C语言程序设计现代方法(第2版)》读书笔记

目录

第五章 选择语句 🚀

5.1.3 逻辑运算符 🚀

5.2 if 语句 🚀

5.2.4 “悬空 else”的问题 🚀

5.2.5 条件表达式 🚀

5.2.6 C89 中的布尔值 🚀

5.2.7 C99 中的布尔值 🚀

5.3 switch 语句 🚀

问与答 🚀


从今天开始,各书读书笔记就陆陆续续开展了哈(本书为《C语言程序设计现代方法(第二版)》)(一本书一个专栏,订阅会第一时间推荐更新哈),主要会把作者认为比较重要或者比较新奇的知识点记录下来。但是要想真的了解一本书,自己去看可能才会有更深的体会哈。


C语言入门篇进阶篇深剖篇都整理在这里这里了哈。然后这里是个人主页,比点头像更好找文章哈。

作者新建立的社区:非科班转码社区-CSDN社区云💖💛💙

期待hxd的支持哈🎉 🎉 🎉


最后是打鸡血环节:改变的确很难,但结果值得冒险,拿出点勇气来。路还很长,现在才刚开始而已。过去无可挽回,未来可以改变。🚀 🚀 🚀

第五章 选择语句 🚀

5.1.3 逻辑运算符 🚀

        逻辑运算符所产生的结果是0 1 。操作数的值经常是 0 1 ,但这不是必需的。逻辑运算符 将任何非零值操作数作为真值来处理,同时将任何零值操作数作为假值来处理。        运算符&& 和运算符 || 都对操作数进行“短路”计算。也就是说,这些运算符首先计算出左 操作数的值,然后计算右操作数;如果表达式的值可以仅由左操作数的值推导出来,那么将不计算右操作数的值。思考下面的表达式: (i != 0) && (j / i > 0) 为了得到此表达式的值,首先必须计算表达式 (i != 0) 的值。如果 i 不等于 0 ,那么需要计算表 达式 (j / i > 0) 的值,从而确定整个表达式的值为真还是为假。但是,如果 i 等于 0 ,那么整 个表达式的值一定为假,所以就不需要计算表达式 (j / i > 0) 的值了。短路计算的优势是显 而易见的,如果没有短路计算,那么表达式的求值将会导致除以零的运算。         运算符! 的优先级和一元正负号的优先级相同,运算符 && 和运算符 || 的优先级低于关系运 算符和判等运算符。例如,表达式 i < j && k == m 等价于表达式 (i < j) && (k == m) 运算符 ! 是右结合的,而运算符 && 和运算符 || 都是左结合的。

5.2 if 语句 🚀

5.2.4 “悬空 else”的问题 🚀

if 语句嵌套时,千万当心著名的“悬空 else ”的问题。思考下面这个例子: 上面的 else 子句究竟属于哪一个 if 语句呢?缩进格式暗示它属于最外层的 if 语句。然而, C 言遵循的规则是 else 子句应该属于离它最近的且还未和其他 else 匹配的 if 语句。在此例中, else子句实际上属于最内层的if 语句(else总是与离他最近的if相配对),所以正确的缩进格式应该如下所示:

为了使else子句属于外层的if语句,可以把内层的if语句用花括号括起来:

这个示例表明了花括号的作用。如果把花括号用在本节第一个示例的 if 语句上,那么就不会有 这样的问题了。

5.2.5 条件表达式 🚀

        条件运算符( conditional operator )由符号 ? 和符号 : 组成,两个符号必须按如下格式一起使用:         表达式1 表达式 2 表达式 3 可以是任何类型的表达式,按上述方式组合成的表达式称为 条件表达式( conditional expression )。条件运算符是 C 运算符中唯一一个要求 3 个操作数的运算符。因此,经常把它称为三元 ternary )运算符(三目操作符)。         条件表达式使程序更短小但也更难以阅读,所以最好避免使用。然而,在少数地方仍会使用条件表达式,其中一个就是return 语句。许多程序员把

替换为

printf函数的调用有时会得益于条件表达式。代码

 可以简化为

条件表达式也普遍用于某些类型的宏定义中( 14.3节)。 

5.2.6 C89 中的布尔值 🚀

多年以来,C语言一直缺乏适当的布尔类型,C89 标准中也没有定义布尔类型。因为许多程 序需要变量能存储 值,缺少布尔类型可能会有点麻烦。 针对 C89 的这一限制,一种解决方法是先声明一个int 型变量,然后将其赋值为 0 1 虽然这种方法可行,但是它对于程序的可读性没有多大贡献,因为没有明确地表示 flag 的赋值 只能是布尔值,并且也没有明确指出 0 1 就是表示假和真。 为了使程序更加便于理解, C89 的程序员通常使用 TRUE FALSE 这样的名字定义宏:

现在对flag的赋值有了更加自然的形式:

为了判定flag是否为真,可以用

或者只写 

后一种形式更好,一是它更简洁,二是当 flag 的值不是 0 1 时程序也能正确运行。         为了判定flag 是否为假,可以用

为了发扬这一思想,甚至可以定义一个可用作类型的宏:

现在就非常清楚 flag 不是普通的整型变量,而是表示布尔条件。(当然,编译器仍然把 flag 成是 int 型变量。)在后面的章节中,我们将介绍一些更好的方法,可以使用类型定义( 7.5 节)和枚举( 16.5 节)在 C89 中设置布尔类型。

5.2.7 C99 中的布尔值 🚀

        长期缺乏布尔类型的问题在C99 中得到了解决。C99提供了 _Bool ,所以在 C 语言的 这一版本中,布尔变量可以声明为:         _Bool是整数类型(更准确地说是 无符号整型),所以_Bool 变量实际上就是整型变量;但是和一般的整型不同,_Bool 只能赋值为 0 1 。一般来说, _Bool变量中存储非零值会导致变量赋值为1         对于_Bool 变量来说,算术运算是合法的(不过不建议这样做),它的值也可以进行打印(显示0 或者 1 )。当然, _Bool 变量也可以在 if 语句中测试:

        除了_Bool 类型的定义,C99还提供了一个新的头 ,这使得操作布尔值更加容易。该头提供了bool 宏,用来代表 _Bool 。如果程序中包含了 ,可以这样写:

头还提供了truefalse两个宏,分别代表10。于是可以写

头使用起来非常方便,因此在后面的程序中,需要使用布尔变量时都用到了这个头。 

5.3 switch 语句 🚀

        在日常编程中,常常需要把表达式和一系列值进行比较,从中找出当前匹配的值。在5.2 节已经看到,级联式if 语句可以达到这个目的。例如,下面的级联式 if 语句根据成绩的等级显示出相应的英语单词:         C语言提供了 switch 语句作为这类级联式 if 语句的替换。下面的 switch 语句等价于前面的 级联式 if 语句: 执行这条语句时,变量 grade 的值与 4 3 2 1 0 进行比较。例如,如果值和 4 相匹配,那么显示信息Excellent ,然后 break 语句( 6.4 节)把控制传递给 switch 后边的语句。如果 grade 的值和列出的任何选择都不匹配,那么执行default分支的语句,显示消息Illegal grade         switch语句往往比级联式 if语句更容易阅读。此外,switch语句往往比if语句执行速度快,特别是在有许多情况要判定的时候。 switch 语句的最常用格式如下:

switch语句十分复杂,下面逐一看一下它的组成部分。

          控制表达式。switch后边必须跟着由圆括号括起来的整型表达式。C语言把字符( 7.3节)当成整数来处理,因此在switch语句中可以对字符进行判定。但是,不能用浮点数和字符串。          分支标号。 每个分支的开头都有一个标号,格式如下:          常量表达式 constant expression )很像是普通的表达式,只是不能包含变量和函数调用。因此,5 是常量表达式, 5 + 10 也是常量表达式, n + 10不是常量表达式(除非n 表示常量的宏)。分支标号中的常量表达式的值必须是整数(字符也可以)。          语句。 每个分支标号的后边可以跟任意数量的语句。不需要用花括号把这些语句括起来。(好好享受这一点,这可是C 语言中少数几个不需要花括号的地方。)每组语句的最后一条通常是break 语句。         C语言不允许有重复的分支标号,但对分支的顺序没有要求,特别是 default 分支不一定要放置在最后。

 

case后边只可以跟随一个常量表达式。但是,多个分支标号可以放置在同一组语句的前面:

为了节省空间,程序员有时还会把几个分支标号放置在同一行中:

可惜的是,C语言不像有些编程语言那样有表示数值范围的分支标号。 

        switch语句不要求一定有 default分支。如果default不存在,而且控制表达式的值和任 何一个分支标号都不匹配的话,控制会直接传给switch语句后面的语句。

 

break 语句的作用 

        现在仔细讨论一下break 语句。正如已经看到的那样,执行 break语句会导致程序“跳”出 switch语句,继续执行switch后面的语句。         需要break 语句是由于 switch 语句实际上是一种“基于计算的跳转”。对控制表达式求值时,控制会跳转到与switch 表达式的值相匹配的分支标号处。分支标号只是一个说明 switch 内部位置的标记。在执行完分支中的最后一条语句后,程序控制“向下跳转”到下一个分支的第一条语句上,而忽略下一个分支的分支标号。如果没有break 语句(或者其他某种跳转语句),控制将会从一个分支继续到下一个分支。思考下面的switch语句:

 

如果grade的值为3,那么显示的消息是 

        故意从一个分支跳转到下一个分支的情况是非常少见的,因此明确指出故意省略break 语句的情况是一个好主意:

如果没有注释,将来某人可能会通过增加多余的break语句来修正“错误”。                                   虽然switch语句中的最后一个分支不需要break语句,但通常还是会放一个break语句在那里,以防止将来增加分支数目时出现“丢失break”的问题。

问与答 🚀

问:当我用=代替==时我所用的编译器没有发出警告。是否有办法可以强制编译器注意这类问题?p.52 

答:下面是一些程序员使用的技巧:他们习惯性地将

现在假设运算符= =意外地写成了=:

因为不可能给 0 赋值,所以编译器将会产生一条错误消息。我没有用这种技巧,因为我觉得这样会使程序看上去很不自然。而且,这种技巧也只能在判定条件中的一个操作数不是左值的时候使用。         幸运的是,许多编译器可以检测出if条件中 =运算符的可疑使用。例如,GCC编译器会在选中-Wparentheses选项或-Wall(所有情况都警告)选项时执行这样的检查。GCC允许程序员通过在if条件外面增加一对圆括号的方式来抑制该警告:

问:如果iint型变量,而ffloat型变量,那么条件表达式i > 0 ? i: f是哪一种类型的值?

答:如问题中出现的那样,当 int 型和 float 型的值混合在一个条件表达式中时,表达式的类型为 float型。如果i > 0 为真,那么变量 i 转化为 float 型后的值就是表达式的值。

问:为什么C99为布尔类型提供了一个更好的名字?p.58

答: _Bool 不算好名字,是吧?C99没有采用 bool boolean 这些更常见的名字,这是因为现有的 C程序中可能已经定义了这些名字,再使用可能会导致编译错误。

问:明白了。但是为什么 _Bool 这个名字就不会影响已有的程序呢? 答: C89 标准指出,以下划线后跟一个大写字母开头的名字是保留字,程序员不应该使用。

*问:本章中的switch语句模板被称为是“最常用格式”,是否有其他格式呢?p.60

答:事实上,本章中描述的 switch 语句格式极具一般性,但 switch 语句还有更具一般性的格式。例如,switch语句包含的标号( 6.4 节)前面可以不放置单词 case ,这可能会产生有趣的(?)陷阱。假设意外地拼错了单词default

因为编译器认为defualt是一个普通标号,所以不会检查出错误。 

最后的最后,创作不易,希望读者三连支持💖

赠人玫瑰,手有余香💖