为了减少程序的计算量,在进行逻辑运算时,C语言采用了在保持原逻辑运算正确性的同时,通过在已确定表达式的确切值的情况下,不计算其后式子的方式来削减运算量,这就是所谓“逻辑短路”
为何要进行逻辑短路
& 和 | 运算的法则
- 我们都知道
0 & 1 == 0, 0 | 1 == 1,其实在这个过程中,无论是&还是|,符号两边的式子都被计算了一次,但对于第一个式子来说,只要第一个式子的结果是false,在进行&运算时,不管其后的结果如何,最后的结果一定为true,相应的,对于|,只要第一个式子是true,其后的结果一定为true.如果将这个性质放在日常代码实现中,我们可以看到
#include<stdio.h>
int a[4];
int main(){
++a[0] | ++a[1] | ++a[2] | a[3];
for(int i=0;i<4;++i) printf("a[%d]==%d ",i,a[i]);
puts("");
return 0;
}
- 运行结果:
注: 前置
++:先进行自增,再将自增后的结果加入逻辑运算中 后置++:先加入逻辑运算,结果完成后再进行自增
- 但是对于整个式子来说,其实我们只要知道第一个是
true,这个表达式的结果便已经显而易见,为了实现这种所谓“逻辑短路”的做法,C语言为我们提供了相应的运算符:&&和||
&&和||运算
&&运算规律和&相同,||运算规律和|相同- 可视为二者的“加强版”
- 同样的式子,我们依然用代码举例
#include<stdio.h>
int a[4];
int main(){
++a[0] || ++a[1] || ++a[2] || a[3];
for(int i=0;i<4;++i) printf("a[%d]==%d ",i,a[i]);
puts("");
return 0;
}
- 运行结果:
- 另外的版本:
#include<stdio.h>
int a[4];
int main(){
++a[0] && 0 && ++a[2] && ++a[3];
for(int i=0;i<4;++i) printf("a[%d]==%d ",i,a[i]);
puts("");
return 0;
}
- 运行结果:
- 在上面的代码中,
a[0]的运算结果为1,&&不进行短路,这时候进行一次运算得到0,在第二个&&时因为前面的式子结果为0,其后的式子一律不参与计算,即a[2]和a[3]的自增无意义
更复杂些的式子
- 现在你已经掌握了对逻辑短路最基本的认识,为了方便以后样例的解释,我先贴出C语言中常用的运算符优先级表
- 我们可以举别的更加有趣的例子:
- 下面式子的值是多少?
#include<stdio.h>
int a[4];
int main(){
a[1]=4,a[2]=1,a[3]=0;
a[0]= a[1]++==4 || a[2]++&&a[3]++;
a[0]== ? a[1]==? a[2]==? a[3]==?
return 0;
}
- 显然不用理睬最后的取值符号,即使
++的优先级高于==,在进行逻辑运算的时候原则一定优先进行逻辑判断,在这个式子中a[1]==4结果为true,随后a[1]自增为5,前面的语句结束 - 这时候
||的前面结果为true,在存在逻辑短路的情况下,依旧不用管高于它优先级的++和&&,直接整个式子判断为true,后续的自增运算符全部作废,显然易得答案 a[0]==1 a[1]==5 a[2]==0 a[3]==0- 按照相似的原理,自然可以依旧举出一个类似的简单例子
#include<stdio.h>
int a[6];
int main(){
a[0]++ && a[1]++ || a[2]++ && a[3]++ || a[4]++ && a[5]++;
//各个变量的值分别是多少?
return 0;
}
- 在逻辑表达式中
&&和||并存时,一方面要注意二者的优先级关系,另一方面要注意逻辑短路的情况,而这类式子谨遵一个重要原则: ==只要能确定表达式的结果,还没有进行的运算不再继续== - 如何理解?
- 来看示例中已经给出的例子
- 首先进行逻辑运算
a[0]&&,前面的值为0,其后的a[1]++忽略,a[0]自增,接着到||,因为前面的值为0无法进行短路,故执行a[2]&&,值为0,其后a[3]++忽略,a[2]自增,最后二者0 || 0 == 0,无法进行短路,执行a[4]&&,结果以此类推,最后结果为a[0]==1 a[1]==0 a[2]==1 a[3]==0 a[4]==1 a[5]==0 - 真的要造这类无意义又笨蛋的一长串式子很简单,如果真的喜欢钻研建议读者自己造并运行代码进行验证,下课!