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);
&&
优先级比||
高,&&
左边的b
先被计算,得到结果为0
。&&
只要有任意一边为0
,整个表达式的结果都为0
。于是b && a
的结果为0
。现在的表达式为:a || 0
。||
右边的b && a
已经有了结果0
,现在要计算||
左边的表达式。a
为1
,整个表达式的结果是:1 || 0
。||
任意一边为1
,整个表达式的结果都为1
。于是最终返回1
。
第三条语句
printf("%d\n", a || b && b);
- 同样先执行
&&
左边的b
,得到结果为0
,于是表达式b && b
的结果为0
。 a
为1
,表达式为:1 || 0
。返回结果1
。
第 2 个示例
#include <stdio.h>
int main()
{
int a = 1, b = 0;
printf("%d\n", b || a && a);
return 0;
}
/*
运行结果:
1
*/
b || a && a
&&
的优先级比||
高,会先去执行&&
左边的a
,结果为1
。表达式为:b || 1 && a
。- 继续计算
&&
右边的a
,结果同样为1
,a && a
的结果为1
。现在的表达式为:b || 1
。 ||
的右边为1
了,它就判定整个表达式为1
,不会再去管左边的b
是什么结果了,||
发生短路。- 直接返回
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 && c && b)
中,先执行a && c
,已知a = 1, c = 1
,因此表达式的值为1
。(a && c && b)
变成了:(1 && b)
。- 执行
(1 && b)
中的b
,b=0
,因此表达式为1 && 0
。有一边为0
,整个表达式为0
。 - 至此,
b || a && c && b
中,||
右边的表达式已经计算完毕,值为0
。现在表达式为:b || 0
。 b=0
,表达式的结果是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)
这个表达式。 (a+=10)
执行完后,a
被赋值为11
,表达式结果为1
。||
遇到左边的为1
,就直接短路,不会去管后面的结果是什么。整个表达式的结果都为1
。将1
赋值给c
,并结束本条语句。
第二条语句
c = (a+=10) || ( (b+=5) && (b+=5) );
- 在第一条语句基础上,把
||
后面的语句用小括号再次包裹了起来。现在变成了 2 个小括号语句:(a+=10) || ( ... )
。(不再是第一条语句那样的 3 个小括号语句了!) - 依然根据算术运算的进栈规则,首先遇到的还是
(a+=10)
这个表达式。 - 其他的执行顺序,与第一条语句一致。
第三条语句
c = ( (a+=10) || (b+=5) ) && (b+=5);
- 还是根据算术运算的进栈规则,首先遇到的语句是:
( (a+=10) || (b+=5) )
。(&& (b+=5)
会稍后执行)。 - 先执行
( (a+=10) || (b+=5) )
中的(a+=10)
,a
被赋值为11
,表达式的结果为1
。该表达式变成了:( 1 || (b+=5) )
。 ( 1 || (b+=5) )
中,||
的左边为1
。触发||
短路特性,不会去理会后面的(b+=5)
语句,整个表达式的结果为1
。- 现在的语句是:
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 个小括号包裹着。根据算术运算的进栈规则,第一个遇到的是:
(a = 3)
。 a
被赋值为3
,表达式的执行结果为1
,现在表达式为:c = 1 || (a = 4) && (b = 5)
。||
左边为1
,触发短路特性。- 不会再去管
||
右边一大坨的(a = 4) && (b = 5)
。直接将1
赋值给c
,并结束本条语句。