C 语言中 &&
和 ||
优先级、结合性
分析下题的答案
C 语言运算符的优先级和结合性
首先,要知道 C 语言运算符的优先级和结合性。
查阅资料得知:&&
的优先级比 ||
高,结合性是从左往右。运算时,会把 &&
左边的表达式看做一个整体。
分析几个简单的示例
第 1 个示例
先来几个简单的示例,进行分析:
#include <stdio.h>
int main()
{
int a = 1, b = 0;
printf("%d\n", b && a || b);
printf("%d\n", a || b && a);
printf("%d\n", a || b && b);
return 0;
}
/*
运行结果:
0
1
1
*/
第一条语句
printf("%d\n", b && a || b);
&&
优先级比||
高,会先执行b
这个表达式。b
的结果为0
,C 语言中0
为假。&&
的特性:只要任何一边的结果为假,整个表达式的结果即为假。b
的结果已经为假,就不会再去理会右边整个a || b
,直接短路并返回结果,不往后面走了。
第二条语句
printf("%d\n", a || b && a);
&&
优先级比||
高,&&
左边的所有内容都会被看做为一个整体,先去执行&&
左边的表达式a || b
。a
为真,||
一样也会短路,任意表达式为真,整个表达式即为真。||
左边已经短路了,同样也不会再去管右边的表达式b && a
了。直接返回1
,结束本条语句。
第三条语句
printf("%d\n", a || b && b);
- 同样先去执行
&&
左边的a || b
表达式。 a
为1
,||
短路并返回结果,后面的表达式不会再去计算了。
第 2 个示例
#include <stdio.h>
int main()
{
int a = 1, b = 0;
printf("%d\n", b || a && a);
return 0;
}
/*
运行结果:
1
*/
b || a && a
&&
的优先级比||
高,会先去执行&&
左边的b || a
。(注:b || a
是&&
左边的一个整体)。b || a
中,同样也先执行左边的b
,b
为0
,此时||
不会短路,它要继续去计算右边的结果。右边的a
为1
,整个b || a
表达的结果为1
。- 现在的表达式是:
1 && a
。a
为1
,&&
的两边都为1
,表达式为1
。 - 计算完毕,返回结果
1
。
第 3 个示例
#include <stdio.h>
int main()
{
int a = 1, b = 0, c = 1;
printf("%d\n", b || a && c && b);
return 0;
}
/*
运行结果:
0
*/
表达式 b || a && c && b
中,有两个 &&
,按照结合性,是从左往右计算。
- 把表达式劈成两瓣,就是:
b || a && c
和&& b
。 - 先去执行
b || a && c
,再次把这个表达式劈开:b || a
和&& c
。 - 从左往右,先会计算
b || a
这个表达式的左边b
,b
的结果为0
,然后计算a
,a
为1
,于是b || a
的结果为1
。现在表达式为:1 && c
,c
也是1
。表达式b || a && c
的结果为1
。 - 现在的表达式为:
1 && b
。&&
需要计算两边的结果才能判定是否都为1
。但是,b
为0
。只要有一个结果为0
,整个&&
表达式就为0
。返回结果0
。
小结
&&
和 ||
同时在一条语句中,要记住 &&
的优先级比 ||
高,&&
左边的所有一切都是一个完整的整体。
再来一题
#include <stdio.h>
int main()
{
int a = 1, b = 0, c = 0;
c = (a+=10) || (b+=5) && (b+=5);
printf("a=%d, b=%d, c=%d\n", a, b, c);
a = 1, b = 0;
c = (a+=10) || ( (b+=5) && (b+=5) );
printf("a=%d, b=%d, c=%d\n", a, b, c);
a = 1, b = 0;
c = ( (a+=10) || (b+=5) ) && (b+=5);
printf("a=%d, b=%d, c=%d\n", a, b, c);
return 0;
}
/*
运行结果:
a=11, b=0, c=1
a=11, b=0, c=1
a=11, b=5, c=1
*/
第一条语句
c = (a+=10) || (b+=5) && (b+=5);
- 有三个“加后赋值语句”,都被小括号包裹了起来。三个优先级是平级的,就会去看
&&
和||
的优先级。 &&
高于||
,先会去执行&&
左边的(a+=10) || (b+=5)
这个表达式。- 再在这个
(a+=10) || (b+=5)
表达式中,先执行(a+=10)
,现在a
的结果为11
,且运行结果为真,即为1
。 - 上述的表达式变成了
1 || (b+=5)
,遇到一个结果为真,||
发生短路,不会再去管后面的(b+=5)
,更不会去管||
后面的那些整体了。 - 直接给
c
赋值为1
,并结束本条语句。
第二条语句
c = (a+=10) || ( (b+=5) && (b+=5) );
- 在第一条语句基础上,把
||
后面的语句用小括号再次包裹了起来。现在变成了 2 个小括号语句:(a+=10) || ( ... )
。 - 先执行
||
左边的小括号中的表达式,a = a + 10
,a
的结果为11
。表达式运行结果为真,即为1
。 - 现在表达式是:
c = 1 || ( (b+=5) && (b+=5) );
。||
发生短路并将1
赋值给c
后,结束整条语句。
第三条语句
c = ( (a+=10) || (b+=5) ) && (b+=5);
- 小括号提升优先级,语句是:
c = ( ... ) && (b+=5);
。 - 先执行
&&
左边小括号中的语句( (a+=10) || (b+=5) )
。 - 在
( (a+=10) || (b+=5) )
中,先执行(a+=10)
。a
被赋值为11
,语句运行结果为1
,||
发生短路,不会去计算后面的(b+=5)
。但是,此条语句中的(b+=5)
是被包裹在小括号中的。不执行(b+=5)
该条语句,是因为被||
短路了。出了小括号之后,还会有... && (b+=5)
这条语句的&&
右边需要去被执行。 - 现在的语句是:
c = 1 && (b+=5);
。左边的结果为真,但&&
还需要右边的结果。b+=5
,b
被赋值为5
,语句运行结果为1
。 - 两边都为
1
,将1
赋值给c
。
回到最初的问题
#include <stdio.h>
int main()
{
int a = 2, b = 2, c = 2;
c = (a = 3) || (a = 4) && (b = 5);
printf("a=%d, b=%d, c=%d\n", a, b, c);
return 0;
}
/*
运行结果:
a=3, b=2, c=1
*/
c = (a = 3) || (a = 4) && (b = 5);
- 语句也是由 3 个小括号包裹着。3 个小括号优先级是平级的,那么就要看
&&
和||
的优先级谁更高,显然是&&
的优先级高。 - 于是表达式
(a = 3) || (a = 4)
这个整体会先被执行。 (a = 3) || (a = 4)
这个整体中,又先执行(a = 3)
。给a
赋值为3
,表达式运行结果为1
(即为真)。只要一个条件为真,||
就会发生短路,不再去理会||
后面(a = 4) && (b = 5)
这一大坨东西。- 把
1
赋值给c
,结束本条语句。
参考文献
C 语言运算符的优先级和结合性