谜题1.6 操作符的优先级和求值顺序
请问,下面这个程序的输出是什么?
#define PRINT3(x,y,z) \
printf(#x "=%d\t" #y "=%d\t" #z "=%d\n",x,y,z)
main()
{
int x, y, z;
x = y = z = 1;
++x || ++y && ++z; PRINT3(x,y,z); (1.6.1)
x = y = z = 1;
++x && ++y || ++z; PRINT3(x,y,z); (1.6.2)
x = y = z = 1;
++x && ++y && ++z; PRINT3(x,y,z); (1.6.3)
x = y = z = -1;
++x && ++y || ++z; PRINT3(x, y,z); (1.6.4)
x = y = z = -1;
++x || ++y && ++z; PRINT3(x,y,z); (1.6.5)
x = y = z = -1;
++x && ++y && ++z; PRINT3(x,y,z); (1.6.6)
}

输出:
x=2 y=1 z=1 (1.6.1)
x=2 y=2 z=1 (1.6.2)
x=2 y=2 z=2 (1.6.3)
x=0 y=-1 z=0 (1.6.4)
x=0 y=0 z=-1 (1.6.5)
x=0 y=-1 z=-1 (1.6.6)

解惑1.6 操作符的优先级和求值顺序
1.6.1
|
初始值:x=1,y=1,z=1 |
|
|
++ x || ++ y && ++ z |
|
|
((++ x) || ((++ y) && (++z))) |
把操作数绑定到操作符上。 |
|
(2 || ((++ y) && (++z))),此时x=2 |
按从左到右的顺序依次求值。 |
|
(TRUE || 任意值) |
因为“||”操作符的左操作数是TRUE,所以没有必要继续求值了。事实上,C语言肯定不会继续求值——按照C语言里的有关规则,在按从左到右的顺序对一个逻辑表达式求值的时候,只要知道了它的实际结果,就不会再对其余部分求值。具体到这道谜题,这意味着y和z的值仍将是1。 |
|
TRUE,即1 |
1.6.2
|
初始值:x=1,y=1,z=1 |
|
|
++ x && ++ y || ++z |
|
|
((TRUE && (++ y))||(++z)),此时x=2 |
|
|
((TRUE && TRUE)||(++z)),此时y=2 |
按从左到右的顺序依次求值。 |
|
TRUE,即1 |
变量z的值没有发生变化。 |
1.6.3
|
初始值:x=1,y=1,z=1 |
|
|
++ x && ++ y && ++z |
|
|
(((++ x) && (++ y)) && (++z)) |
|
|
((2 && 2) && (++z)),此时x=2,y=2 |
|
|
(TRUE && (++z)) |
|
|
TRUE,即1 |
1.6.4
|
初始值:x= -1,y= -1,z= -1 |
|
|
++ x && ++ y || ++ z |
|
|
(FALSE || (++z)) |
因为“&&”操作符的左操作数是FALSE,所以没有必要对++y求值。可是,“||”操作的结果现在还不能确定。 |
|
(FALSE || (0)),此时z=0 |
1.6.5
|
初始值:x= -1,y= -1,z= -1 |
|
|
++ x||++ y && ++z |
|
|
(FALSE||((++ y) && (++z))),此时x=0 |
1.6.6
|
初始值:x= -1,y= -1,z= -1 |
|
|
++ x && ++ y && ++z |
|
|
((FALSE&&(++ y)) && (++z)),此时x=0 |
关于逻辑操作符的副作用:正如你们现在已经体会到的那样,C语言里的逻辑表达式的求值有一定的难度,因为是否需要对逻辑操作符的右操作数求值取决于其左操作数的求值结果。这种根据具体情况来决定是否对右操作数求值的做法是逻辑操作的一个有用的属性。可是,如果在逻辑表达式的右半部分隐藏着副作用,那么就难免会留下隐患——那些副作用可能会发作,也可能不会发作。一般说来,谨慎对待副作用总是没错的,这在逻辑表达式中就更为重要了。






