20

我是 C 编程语言的初学者,最近我读到了逻辑与&&运算符。

我也知道,在 C 编程语言中,所有非零值都被视为TRUE

NON-ZERO && NON-ZERO = 1  
NON-ZERO && ZERO = 0  
ZERO && NON-ZERO = 0
ZERO && ZERO = 0  

但是当我处理以下程序时,我没有得到预期的答案。

int main(){  
  int x, y, z;  
  x = y = z = -1;  
  y = ++x && ++y && ++z;  
  printf("x = %d, y = %d, z = %d, x, y, z);  
  return 0;  
} 

我期待

x = 0, y = 0, z = 0 

但答案是

x = 0, y = 0, z = -1

谁能解释一下,为什么我得到这个答案?

编辑:在这个问题中,我没有询问运算符的优先级。

4

5 回答 5

22

Because of Short-circuit evaluation, when x is 0, y and z don't really need to be evaluated since 0 && ANYTHING is 0.

Once x is incremented to 0, the result is 0, and that's what y gets.

z remains unchanged (-1).


 x  | y  |  z 
----+----+-----
 -1 | -1 | -1   //x = y = z = -1;  
  0 | -1 | -1   //++x && ... Now the whole expression is evaluated to 0
  0 |  0 | -1   //y = ++x && ++y && ++z;
于 2013-08-14T10:51:42.443 回答
14

I only can think about that && evaluates in short circuit: given A && B, if A evaluates false then B is not evaluated.

So:

X becomes 0. && ++y && ++z does not evaluates since X/0/false && ...

y=0 as assigned from y = x/0/false

z remains unmodified since ++z does not get executed.

于 2013-08-14T10:50:10.313 回答
4

&&运算符是成对评估的,因此我猜 C 正在评估

((++x && ++y) && ++z)

现在,++x将返回零,因此第一个&&将失败,第二个也将失败,而无需评估++yor ++z

y = 0因为这是表达式的结果。

z没有被触动

于 2013-08-14T10:53:12.780 回答
4

会发生什么++y并且++z永远不会被评估,因为第一部分已经确定了新值是什么y

您的语句的第一部分++x && ...相当于0 && ...,然后我们已经知道y最后将是 0,因此不执行该语句的其余部分。

如果你这样做:

int main(){  
  int x,y,z,tmp;  
  x = y = z = -1;  
  tmp = ++x && ++y && ++z;  
  printf("x = %d, y = %d, z = %d, tmp = %d", x,y,z, tmp);  
  return 0;  
} 

你会得到x = 0, y = -1, z = -1, tmp = 0

左评估在 C99 标准中得到保证。您可以在部分中找到它6.5.13 Logical AND operator

与按位二进制 & 运算符不同,&& 运算符保证从左到右的求值;在对第一个操作数求值之后有一个序列点。如果第一个操作数比较等于 0,则不计算第二个操作数。

您可以在Wikipedia或 C99 标准的附件 C 中找到有关什么是序列点的更多信息

于 2013-08-14T10:49:55.197 回答
4

为了完整性(大脑转储):

这个巫术背后的术语叫做短路。让我们回顾一下您的代码,然后简要介绍一下为什么会发生这种情况。看着:

int main( void ) {  
  int x, y, z;
  x = y = z = -1;
  y = ++x && ++y && ++z;

  printf( "x = %d, y = %d, z = %d, x, y, z );

  return 0;  
}

...我们开始逐行分解它。第一行:

int x, y, z;

... 声明三个整数x,yz. 它们被初始化为堆栈帧上的垃圾值,因为没有初始化(赋值运算符)。这一行其实无所谓,现在我们来看下一行:

 x = y = z = -1;

...我们看到我们在同一行上执行多个任务。回想一下,赋值运算符将改变赋值运算符左侧的标识符(使用赋值运算符右侧的值)并返回 的值x。这称为赋值重载。但同样,这并不重要——唯一要意识到的重要事情是xy现在z都是 -1。让我们看看下一行:

 y = ++x && ++y && ++z;

......巫术尤达说。让我们添加括号以使首先评估哪个步骤更明显:

 y = ( ( ++x ) && ++y && ++z );

...现在查看最里面的括号,我们看到它是前缀增量 of x,这意味着我们将增加 of 的值x然后返回它。我们注意到它x最初是-1,现在增加后是0。这将解决如下:

 y = ( 0 && ++y && ++z );

...现在重要的是要注意查看我们的真值表:

A | B | A && B
--------------
T | T |   T
T | F |   F
F | T |   F
F | F |   F

...对于AND逻辑运算符,我们看到 F (AND) T, T (AND) F 都是 F。编译器会意识到这一点,并且在评估值是的连接 (AND) 时会短路false——一个聪明的优化技术。然后它将解析为分配y为 0(这是错误的)。回想一下,在 C 中,任何非零值都是true,只有 0 是false。该行将如下所示:

 y = 0;

...现在看下一行:

 printf( "x = %d, y = %d, z = %d, x, y, z );

...现在你应该很明显它会输出x = 0, y = 0, z = -1.

于 2013-08-14T11:04:01.540 回答