7.3 获得逻辑性

对于if和while语句通常怎样使用关系表达式作为判断条件您已经很清楚了。有时您会觉得将两个或多个关系表达式组合起来很有用处。例如,假设需要编写一个程序,用来计算在一个输入的句子中除单引号和双引号以外的字符出现了多少次。可以用逻辑运算符来实现该目的,可以用(英文的)句号(.)来标识一个句子的结束。程序清单7.6用一个简短的程序阐明了这种方法。

程序清单7.6 chcount.c程序

阅读 ‧ 电子书库

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

下面是一个运行示例:

阅读 ‧ 电子书库

首先,程序读入一个字符并检查它是否是一个句号,因为句号标志着一个句子的结束。接下来的语句中,使用了逻辑与运算符&&,这是我们以前没有遇到的。可以将该if语句翻译为:“如果字符不是双引号并且它不是单引号,那么charcount增加1。”

要使整个表达式为真,则两个条件必须都为真。逻辑运算符的优先级低于关系运算符,所以不必使用圆括号组合子表达式。

C有三个逻辑运算符,请参见表7.3:

表7.3 C的逻辑运算符

 

 

运 算 符 含 义
&&
II

假设exp1和exp2是两个简单的关系表达式,例如cat>rat且debt==1000。那么可以声明如下:

● 仅当exp1和exp2都为真的时候,exp1 && exp2才为真。

● 如果exp1或exp2为真或二者都为真,exp1 II exp2为真。

● 如果exp1为假,则!expl为真;并且如果exp1为真,则!exp1为假。

下面是一些具体的例子:

● 5>2&& 4>7为假,因为只有一个子表达式为真。

● 5>2||4>7为真,因为至少有一个子表达式为真。

● !(4>7)为真,因为4不大于7。

顺便说说,最后一个表达式与下面的表达式等价:

阅读 ‧ 电子书库

如果您对于逻辑运算符不熟悉或感到很别扭,请记住:

(练习&&时间)==完美

7.3.1 改变拼写法:iso646.h头文件

C是在美国使用标准美式键盘的系统上发展而来。但在世界各地,并不是所有的键盘都有与美式键盘相同的符号。因此,C99标准为逻辑运算符增加了可供选择的拼写法。它们在iso646.h头文件里定义。如果包含了这个头文件,就可以用and代替&&,用or代替!。用not代替!。例如,下列语句:

阅读 ‧ 电子书库

可以重写为以下方式:

阅读 ‧ 电子书库

表7.4列出了这些选择;很容易记住它们。事实上,您可能想知道为什么C不完全使用这些新术语。回答可能是C历史上一直设法保持尽量少的关键字。参考资料7“扩展的字符支持”中列出了我们还没遇到过的一些运算符的更多可供选择的拼写法。

表7.4 逻辑运算符的可选表示法

 

 

传 统 的 iso646.h
&& and
II or
! not
7.3.2 优先级

!运算符的优先级很高。它高于乘法运算,和增量运算符的优先级相同,仅次于圆括号。&&运算符的优先级高于II,这二者的优先级都低于关系运算符而高于赋值运算。因此,下列表达式:

阅读 ‧ 电子书库

将被认为是这样的:

阅读 ‧ 电子书库

也就是说,b界于a和c之间,或者b大于d。

很多程序员都愿意使用圆括号,正如上面第二种写法那样,尽管这并不是必需的。这样的话,即使您不能完全记住逻辑运算符的优先级,表达式的含义仍然是很清楚的。

7.3.3 求值的顺序

除了那些两个运算符共享一个操作数的情况以外,C通常不保证复杂表达式的哪个部分首先被求值。例如在下面的语句里,可能先计算表达式5+3的值,也可能先计算9+6的值。

阅读 ‧ 电子书库

C语言允许这种不确定性,以便编译器设计者可以针对特定的系统做出最有效率的选择。一个例外是对逻辑运算符的处理。C保证逻辑表达式是从左至右求值的。&&和II运算符是序列的分界点,因此在程序从一个操作数前进到下一个操作数之前,所有的副作用都会生效。而且,C保证一旦发现某个元素使表达式总体无效,求值将立刻停止。这些约定使像下面这样的结构成为可能:

阅读 ‧ 电子书库

这个结构建立一个循环读入字符,直到出现第一个空格符或换行符。第一个子表达式给c赋值,然后c的值被用在第二个子表达式中。如果没有顺序保障,计算机可能试图在c被赋值之前判断第二个子表达式。

下面是另外一个例子:

阅读 ‧ 电子书库

如果number值为0,那么第一个子表达式为假,就不再对关系表达式求值。这就避免了计算机试图把0作为除数。很多语言都没有这个特性,在知道number为0后,它们仍将继续后面的条件检查。

最后,考虑这个例子:

阅读 ‧ 电子书库

&&运算符是序列分界点这一事实保证了在对右边表达式求值之前,先把x的值增加1。