C语言关系、条件和逻辑操作符详解
C语言关系、条件和逻辑操作符详解
关系操作符
C语言中用于比较的表达式称为“关系表达式”(relational expression),其中使用的运算符称为“关系运算符”(relational operator)。主要的关系运算符有以下6个:
>
:大于运算符<
:小于运算符>=
:大于等于运算符<=
:小于等于运算符==
:相等运算符!=
:不相等运算符
关系表达式的例子:
a == b
a != b
a < b
a > b
a <= b
a >= b
关系表达式通常返回0或1,表示真假。例如,如果a
等于b
,表达式会返回1(真);如果a
不等于b
,表达式会返回0(假)。
用代码验证:
2 != 3
返回0(假)2 == 2
返回1(真)20 > 12
返回112 > 20
返回0
“==”与“=”的区别
注意:相等运算符“==”与赋值运算符“=”是两个不同的运算符,不要混淆。
例如,当键盘输入3时:
if (a == 3) {
printf("hehe");
} else {
printf("haha");
}
如果输入3,if
表达式为真,打印“hehe”;如果输入5,if
表达式为假,打印“haha”。
为了避免错误,写判断相等的表达式时,可以将常量写在左边,变量写在右边:
if (3 == a) {
// ...
}
这样,如果误将“==”写成“=”,编译器会报错,因为不能将变量赋值给常量。
多个关系运算符不宜连用
另一个需要避免的错误是:多个关系运算符不宜连用。
例如:
i < j < k
这种写法虽然合法,但通常达不到预期的结果。实际执行的是:
(i < j) < k
因为关系运算符是从左到右计算,所以上面式子中,(i < j)
先计算返回0或1,最终0或1与右边< k
进行比较。
例子:
如果输入的年龄是10,为什么也会打印“青年”呢?因为这段代码是错的,在语法上是对的,但在逻辑上是错的。if
表达式中先算的是:18<=age,而18不可能小于等于10,所以18<=10为假,为假就是0。紧接着0<=40为真,就打印了“青年”。并不是拿age与18和40一起比的。
多个关系运算符判断值是否在中间的写法
如果想要判断变量j
的值是否在i
和k
之间,应该使用下面的写法:
i < j && j < k
例如,我们输入一个年龄,如果年龄在18岁~36岁之间,我们输出青年。此时代码正确,青年就不打印了。
条件操作符
条件操作符也叫三目操作符(C语言中唯一的一个三目操作符),需要接受三个操作数,形式如下:
exp1 ? exp2 : exp3
条件操作符的计算逻辑是:如果exp1
为真,计算exp2
,计算的结果是整个表达式的结果;如果exp1
为假,计算exp3
,计算的结果是整个表达式的结果。
练习1: 使用条件操作符改造下面代码的逻辑。
#include <stdio.h>
int main() {
int a = 0;
int b = 0;
scanf("%d", &a);
if (a > 5) {
b = 3;
} else {
b = -3;
}
printf("%d\n", b);
return 0;
}
改造后:
int main() {
int a = 0;
int b = 0;
scanf("%d", &a);
b = (a > 5 ? 3 : -3);
printf("%d\n", b);
return 0;
}
练习2: 使用条件表达式实现找两个数中较大值。
逻辑操作符
逻辑运算符提供逻辑判断功能,用于构建更复杂的表达式,主要有下面三个运算符:
!
:逻辑取反运算符(改变单个表达式的真假)。&&
:与运算符,就是并且的意思(两侧的表达式都为真,则为真,否则为假)。||
:或运算符,就是或者的意思(两侧至少有一个表达式为真,则为真,否则为假)。
注:C语言中,非0表示真,0表示假。
逻辑取反操作符:!
!
:只有一个操作数,所以是单目操作符。
a | !a |
---|---|
非0(真) | 0(假) |
0(假) | 1(真) |
注:!
不能放在操作数的后面,只能放在操作数的前面。
逻辑与运算符:&&
a | b | a && b |
---|---|---|
非0(真) | 非0(真) | 1(真) |
非0(真) | 0(假) | 0(假) |
0(假) | 非0(真) | 0(假) |
0(假) | 0(假) | 0(假) |
注:全真则真,一假全假。
逻辑或运算符:||
| a | b | a || b |
|---|---|--------|
| 非0(真) | 非0(真) | 1(真) |
| 非0(真) | 0(假) | 1(真) |
| 0(假) | 非0(真) | 1(真) |
| 0(假) | 0(假) | 0(假) |
注:一真则真。
例如:我们说一年中月份是12月或者1月或者2月是冬天,那么我们怎么使用代码体现呢?
练习:闰年的判断
输入一个年份year
,判断year
是否是闰年。
闰年的判断规则:
- 能被4整除并且不能被100整除的是闰年
- 能被400整除的是闰年
可以发现:
- 规则1有“并且”两字,所以规则一是并且的关系:()&&();
- 规则1与规则2都能判断是不是闰年,它们是或者的关系:(这其中包含规则1的并且关系:(()&&())||())
合并起来则为:(()&&())||()
第一种写法:
第二种写法:
int main() {
int year = 0;
scanf("%d", &year);
if ((year % 4 == 0) && (year % 100 != 0)) {
printf("是闰年");
} else {
if (year % 400 == 0) {
printf("是闰年");
} else {
printf("不是闰年");
}
}
return 0;
}
短路
C语言逻辑运算符还有一个特点,它总是先对左侧的表达式求值,再对右边的表达式求值,这个顺序是保证的。如果左边的表达式满足逻辑运算符的条件,就不再对右边的表达式求值。这种情况称为“短路”。
例如:
if (month >= 3 && month <= 5)
表达式中&&
的左操作数是month >= 3
,右操作数是month <= 5
,当左操作数month >= 3
的结果是0的时候,即使不判断month <= 5
,整个表达式的结果也是0(不是春季)。因为逻辑与一假全假,从左边表达式就知道结果,不再对后面表达式进行计算了。
所以,对于&&
操作符来说,左边操作数的结果是0的时候,右边操作数就不再执行。
对于||
操作符是怎么样呢?我们结合前面的代码:
if (month == 12 || month == 1 || month == 2)
如果month == 12
,则不用再判断month
是否等于1或者2,整个表达式的结果也是1(是冬季)。
所以,||
操作符的左操作数的结果不为0时,就无需执行右操作数。
像这样仅仅根据左操作数的结果就能知道整个表达式的结果,不再对右操作数进行计算的运算称为短路求值。
练习:阅读代码,计算代码输出的结果。
因为:
a++
是后置++
:先用后加。在i = a++ && ++b && d++
中,a
为0,后面又是&&
(逻辑与),所以a++
(0)&&++b
为0(假),紧接着后面又是&&
(逻辑与),即a++
&&++b
(0)&&d++
,为0。- 所以
b
,c
,d
不做变化,只有a
在使用过后自加了1,即a=1; b=2; c=3; d=4
。
将a
改为2后的值呢?
因为:
a++
是后置++
:先用后加。在i = a++ && ++b && d++
中,a++
整个表达式为2,是真,所以得算后面的++b
。同时用完后a
自加了1==3。++b
是前置++
:先加后用。所以++b
这个表达式在i = a++ && ++b && d++
中为3,是真,接着算d++
。d++
是后置++
:先用后加。在i = a++ && ++b && d++
中,d++
整个表达式为4,同时用完后自加了1==5。
所以最后的值:a=3, b=3, c=3, d=5
。
注:i
的值为1。因为i = a++ && ++b && d++
都为真,为真就返回了1。
将&&
改为||
,读下面代码:
a++
在i = a++ || ++b || d++
中为2,是真,所以后面短路求值,都不用算了,即b, d
的值都不变。只有a
在用后自加了1为3。
同样的,将a
变为0,代码的结果分别为: