为什么这个if语句结合赋值和相等性检查返回true?


216

我一直在思考一些初学者的错误,最后我得到了if声明中的错误。我对此扩展了一些代码:

int i = 0;
if (i = 1 && i == 0) {
    std::cout << i;
}

我已经看到if语句返回真实的,它couti作为1。如果在if语句中i分配1了if,为什么i == 0返回true


83
伙计们,这不是错字问题。在OP想知道为什么if语句输入这个代码,因为i被设置为1
NathanOliver '19

24
还是分配结果1 && i == 0
JVApen

4
给初学者的建议:他们不应该使用这种“高级”语言构造。只需单独分配变量。这也将避免序列点可能出现的问题。实际代码中的这种代码通常也看起来很糟糕。
user202729 '19

1
这必然会导致面试问题
RAZ_Muh_Taz

Answers:


391

这与运算符优先级有关

if (i = 1 && i == 0)

不是

if ((i = 1) && (i == 0))

因为&&==都比拥有更高的优先级=。真正起作用的是

if (i = (1 && (i == 0)))

将的结果分配1 && (i == 0)i。所以,如果i在开始0然后i == 0true,那么1 && truetrue(或1),然后i被设置为1。然后,由于1为true,请输入if块并打印您分配给的值i


2
@JörgWMittag太酷了。我喜欢这样,它迫使您使用括号。
NathanOliver

基本上,i = !i; if (i)写得
不错

@NathanOliver:Fortress是一种很酷的语言,可以解决很多问题。(首席设计师是Guy L. Steele,所以就不足为奇了。)不幸的是,它没有在DARPA的最后一轮融资中被使用,随后被Oracle封存。
约尔格W¯¯米塔格

6
自然地,向编译器请求少量警告将检测到该错误,
Deduplicator

4
任何不假定int和booleans等价的语言都可以使用。
rghome

16

假设您的代码实际上如下所示:

#include <iostream>
using namespace std;

int main()  {
    int i = 0;
    if (i = 1 && i == 0) {
        cout << i;
    }
}

然后这样:

if (i = 1 && i == 0) {

评估为

 if (i = (1 && i == 0)) {

i设置为1


39
额外的代码真的必要吗?看起来确实很明显,因为否则就无法运行。
Modelmat

13
不仅不必要的额外代码。答案无法清楚地说明运算符的优先级。
弗朗西斯科·扎拉波佐(Frank Francisco Zarabozo)'19年

34
既然我们在尼特皮克火车上...我看到了using namespace std
Mateen Ulhaq,

8
有额外的代码-但它仍然不是错误的代码。答案仍然是正确的。当然,它不能解释运算符的优先级。但是有人会建议添加它,而不是直接投票!
B Charles H

14
哇,-4很苛刻,考虑到这可以正确回答问题,尽管可能不是最佳选择。它没有像其他答案那样扩展运算符的优先级,但是它确实在代码的上下文中说明了足够多的内容,因此任何以为=先于此的人都&&可以看到问题。另外,是的,扩展是无关紧要的,但是我认为它没有那么重要。我不敢相信这种微小的差异会导致人们以151比-4的比例通过投票。
JoL

-4

它与解析从右到左的规则有关。例如,y = x + 5。
所有子表达式都具有重要性。从右到左评估两个具有同等重要性的表达式。首先完成&&表示端,然后是LHS。

我感觉合理。


1
关联性(“从右到左的规则”)与此无关。这是关于优先级(“重要性”),并用该运营商具有相同的优先级。
Lightness Races in Orbit

-4

实际答案是:

  1. 编译器将“ i == 0” 赋予优先级,其结果为true。
  2. 然后它将i = 1评估为TRUE或FALSE,并且由于编译后的赋值运算符永远不会失败(否则它们将不会编译),因此它的评估结果也为true。
  3. 由于两个语句的评估结果均为true,而TRUE && TRUE的评估结果为TRUE,因此if语句的评估结果为TRUE。

作为证明,只需查看您输入的代码的编译器的asm输出(所有注释都是我自己的):

mov     dword ptr [rbp - 8], 0    ; i = 0;
cmp     dword ptr [rbp - 8], 0    ; i == 0?
sete    al                        ; TRUE (=1)
mov     cl, al
and     cl, 1                     ; = operator always TRUE
movzx   edx, cl
mov     dword ptr [rbp - 8], edx  ; set i=TRUE;
test    al, 1                     ; al never changed,
                                  ; so final ans is TRUE

上面的asm输出来自CLANG,但是我看过的所有其他编译器都提供了类似的输出。对于该站点上的所有编译器,无论是纯C还是C ++编译器,都是如此,所有这些编译器都没有任何更改编译器模式的编译指示(对于C ++编译器,默认情况下为C ++)

请注意,您的编译器实际上并未设置i = 1,而是设置i = TRUE(这意味着任何32位非零整数值)。这是因为&&运算符仅评估语句是TRUE还是FALSE,然后根据该结果设置结果。作为证明,尝试将i = 1更改为i = 2,您可以自己观察到什么都不会改变。在Compiler Explorer中使用任何在线编译器亲自查看


1
1)当这个问题用C ++标记时,文档链接到C运算符优先级。两种不同的语言。2a)i = 1是赋值[不是等价]运算符;2b)我可以向您保证,if (i = 0)在C和C ++中都将评估为错误条件,因此,如果评估为“永不失败”,则它是否为true会产生误导。
TrebledJ '19

1
and cl, 1 ; = operator always TRUE<<如果我写错了,请纠正我,但在这里看不到分配。它代表1 &&表达式的一部分。因此,此答案基本上评估为false
锡克

“当这个问题用C ++标记时,文档链接到C运算符优先级。两种不同的语言。”-当您将C和C ++运算符优先级进行比较时,两者之间有什么区别?他们在该主题上具有相同的优先级,这并不奇怪,因为C ++是C的直接派生(或者另一种说法是C是C ++语言的子集,所以他们当然会拥有很多共同点,包括优先顺序)。无论如何我都会修复我的帖子,以防造成混淆。
–'ar18

“如果我错了,请纠正我,但在这里看不到分配”-然后让我纠正您!1是立即值,而不是任何测试或计算的结果。这就是所谓的“假定的TRUE”值。唯一发生的测试是针对i == 0语句的,即–“ cmp dword ptr [rbp-8],0”。仅当它说“ movzx edx,1”时,您才是正确的。根据我前面的所有帖子,应该有两个比较,但在现实生活中只有一个比较,并且每个主要编译器的asm输出都证明这些帖子是完全错误的。
ar18

2
除了弄错优先级(请参阅NathanOliver的答案以获取正确的解析)之外,您还做出了这样的错误声明:赋值运算符始终求值为TRUE。尝试if ( i = 0 ) { print something }。您的回答也自相矛盾。首先,您说要应用i=1之前评估的值&&,然后最后您说要i设置为&&运算符的结果。
MM
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.