
5.6 逻辑运算符与关系运算符
早期的C语言没有明确定义布尔(又写作bool)数据类型,以表示true与false这样的真值和假值。那时,C语言会把零值默认为假,并把不是0的值默认为真,这种默认规则虽然能让我们在写代码时省略一些内容,但我们还是必须谨慎地使用,以免产生与预期不相符的结果。
后来,C语言引入了bool类型,我们只需要在源代码文件里面添加#include<std-bool.h>指令,就可以使用true与false这两个布尔值了。另外,我们自己还可以通过枚举来定义布尔值,或利用自定义的类型来表示布尔值,这两个话题分别在第9章与第10章中讲解。
C语言支持下面三种布尔运算符(也就是逻辑运算符):
□||:二元逻辑或(Logical OR)运算符。
□&&:二元逻辑与(Logical AND)运算符(也叫逻辑和运算符)。
□!:一元逻辑非(Logical NOT)运算符(也叫逻辑反运算符或逻辑取反运算符)。
这些逻辑运算符的结果要么是true(非零),要么是false(零值)。逻辑或(||)跟逻辑与(&&)运算符之所以连用两个相同的字符来表示,是为了同另外两种运算符相区分,那两种运算符只用单个字符来表示,也就是按位或(|)及按位与(&),它们是用来操作二进制位的,这个我们后面会简单地介绍一下。
前两种逻辑运算符可以按照下列格式来连接expressionA与expressionB这两条表达式:

对于逻辑与(&&)运算符来说,如果expressionA是false,那么它就不会再去求expressionB的值(因为无论expressionB是什么值,整个表达式的结果都是false)。如果expressionA是true,那么它必须求出expressionB的值,以确定整个表达式的结果。
对于逻辑或(||)运算符来说,如果expressionA是true,那么它就不会再去求expressionB的值(因为无论expressionB是什么值,整个表达式的结果都是true)。如果expressionA是false,那么它必须求出expressionB的值,以确定整个表达式的结果。
一元的逻辑非(!)运算符直接写在某条表达式的左侧,例如!expressionC。
这个运算符会把expressionC的值自动转换成布尔值,然后把运算结果定为与该布尔值相反的另一种布尔值。因此,!true的值是false,!false的值是true。
下面我们要写一个名叫logical.c的程序,这个程序会打印三张表格,以展示每一种逻辑运算符的求值规则。这些表格叫作真值表(truth table)。我们会把结果用1或0这样的数字来展示,但实际上,它们代表的是对应的布尔值,也就是true(真)与false(假)。首先打印逻辑与(AND)运算符的真值表,这张表格用printLogicalAND()函数来显示:

接下来打印逻辑或(OR)运算符的真值表,这张表格用printLogicalOR()函数来显示:

最后打印逻辑非(NOT)运算符的真值表,这张表格用printLogicalNOT()函数来显示:

请创建一份名为logicals.c的文件,并把刚才那三个函数的代码录入该文件,然后将下面这段代码写在那三个函数上方,这样就把logicals.c程序写完了:

请保存并编译logicals.c文件,然后运行程序。你应该会看到下面这样的输出结果。

这些表格就是真值表。如果要对第一行中的某个值与第一列中的某个值做AND或OR运算,那你就沿着第一个值向下查,并沿着第二个值向右查,这两个方向交汇处的那个单元格指的就是这次运算的结果。例如1 AND 1等于1,1 OR 0等于1。NOT运算直接从第一列中的某个值向右查,就可以查到结果,例如NOT 0等于1。
并不是所有的运算都可以用这几种逻辑运算符来实现。有时我们想判断两个值之间是否有某种关系,并把判断结果表示成true或false,这样我们就可以将这个结果用在if或while结构中,以决定程序的走向。这样的判断可以由关系运算符(也称为比较运算符)来做。
关系运算符用来比较某个表达式的值与另一个表达式的值,确定它们之间是否存在某种关系。它们都是二元运算符,因此同逻辑与(&&)及逻辑或(||)运算符一样,需要写在两个表达式之间。这些运算符的计算结果都是布尔值。这样的运算符一共有6个:
□>(大于):如果expressionA大于expressionB,那么结果为true。
□>=(大于或等于):如果expressionA大于或等于expressionB,那么结果为true。
□<(小于):如果expressionA小于expressionB,那么结果为true。
□<=(小于或等于):如果expressionA小于或等于expressionB,那么结果为true。
□==(等于。注意,这是两个等号,而不是一个。单个的等号是赋值运算符):如果expressionA等于expressionB,那么结果为true。
□!=(不等于):如果expressionA不等于expressionB,那么结果为true。
等我们在后续章节里讲到if、for与while语句的时候,再详细讨论这些运算符。