为啥这个结合赋值和相等检查的 if 语句返回 true?

Posted

技术标签:

【中文标题】为啥这个结合赋值和相等检查的 if 语句返回 true?【英文标题】:Why does this if-statement combining assignment and an equality check return true?为什么这个结合赋值和相等检查的 if 语句返回 true? 【发布时间】:2019-10-09 09:44:05 【问题描述】:

我一直在考虑一些初学者的错误,最后我发现了if 声明中的错误。我将代码扩展了一下:

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

我看到if 语句返回true,它couti1。如果i在if语句中赋值为1,为什么i == 0返回true

【问题讨论】:

伙计们,这不是一个错字问题。 OP 想知道为什么使用此代码输入 if 语句,因为 i 设置为 1 还是赋值1 &amp;&amp; i == 0的结果? 给初学者的建议:他们不应该使用这种“高级”的语言结构。只需单独分配变量。这也将避免序列点可能出现的问题。实际代码中的这种代码通常也会看起来很糟糕。 这肯定是面试题 【参考方案1】:

这与operator precedence 有关。

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

不是

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

因为&amp;&amp;== 的优先级都高于=。它真正起作用的是

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

1 &amp;&amp; (i == 0) 的结果分配给i。所以,如果i0 开头,那么i == 0 就是true,所以1 &amp;&amp; true 就是true(或1),然后i 被设置为1。然后由于1 为真,您输入if 块并打印您分配给i 的值。

【讨论】:

@JörgWMittag 这很酷。我喜欢它强迫你使用括号。 基本上,i = !i; if (i) 写得好 @NathanOliver:Fortress 是一种非常酷的语言,它把很多事情都做对了。 (首席设计师是 Guy L. Steele,所以这并不奇怪。)不幸的是,它没有在 DARPA 的最后一轮融资中被选中,随后被甲骨文封存。 当然,向编译器请求少量警告就会检测到该错误, 任何不假定 int 和 booleans 等价的语言也可以选择这个。【参考方案2】:

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

#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

【讨论】:

额外的代码真的有必要吗?情况似乎很明显,否则它不会运行。 不仅是不必要的额外代码。答案未能清楚地解释运算符优先级。 既然我们在挑剔的火车上......我看到了using namespace std 有额外的代码 - 但它仍然不是错误的代码。答案仍然是正确的。当然,它并没有解释运算符的优先级。但有人可能会建议添加它,而不是直接投反对票! 哇,-4 太苛刻了,考虑到这可以正确回答问题,尽管可能不是最佳的。它没有像其他答案那样扩展运算符优先级,但它确实在代码的上下文中说明了足够多的内容,因此任何认为= 出现在&amp;&amp; 之前的人都可以看到这个问题。另外,是的,扩展是无关紧要的,但我认为这并不重要。我无法相信如此微小的差异会导致人们以 151 票对 -4 票投赞成票。【参考方案3】:

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

对我来说很有意义。

【讨论】:

关联性(“从右到左的规则”)与此无关。这是关于优先级(“重要性”),使用的运算符具有相同的优先级。【参考方案4】:

实际答案是:

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

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

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++ 编译器,都没有任何 pragma 来更改编译器的模式(对于 C++ 编译器,默认情况下是 C++)

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

上使用任何在线编译器亲自查看

【讨论】:

1) 当这个问题用 C++ 标记时,文档链接到 C 运算符优先级。两种不同的语言。 2a) i = 1 是一个赋值[不是等价]运算符; 2b) 我可以向您保证,if (i = 0) 在 C 和 C++ 中都将评估为错误条件,因此它是否评估为 true wrt wrt “它永远不会失败”有点误导。 and cl, 1 ; = operator always TRUE 1 && 部分。所以这个答案基本上评估为false “当这个问题用 C++ 标记时,文档链接到 C 运算符优先级。两种不同的语言”——当您将 C 与 C++ 运算符优先级进行比较时,两者之间有什么区别?他们在这个主题上有相同的优先级,这并不奇怪,因为 C++ 是 C 的直接派生(或者换一种说法,C 是 C++ 语言的子集,所以他们当然会有很多共同点,包括优先级)。无论如何,我都会修复我的帖子,以防万一。 “如果我错了,请纠正我,但我在这里看不到任何分配”——然后让我纠正你! 1 是一个立即值,而不是任何测试或计算的结果。这就是所谓的“假定的 TRUE”值。唯一发生的测试是针对 i==0 语句,即 -- "cmp dword ptr [rbp - 8], 0"。只有当它说“movzx edx,1”时你才是正确的。根据我之前的所有帖子,应该有两个比较,但在现实生活中只有一个,每个主要编译器的 asm 输出都证明这些帖子完全不正确。 除了优先级错误(请参阅 NathanOliver 的正确解析答案),您还错误地声称赋值运算符始终评估为 TRUE。试试if ( i = 0 ) print something 。您的回答也自相矛盾;一开始你说i=1&amp;&amp;被应用之前被评估,然后你说i被设置为&amp;&amp;操作符的结果。

以上是关于为啥这个结合赋值和相等检查的 if 语句返回 true?的主要内容,如果未能解决你的问题,请参考以下文章

Python中的if语句——参考Python编程从入门到实践

if判断语句

if语句

python随笔4(if语句)

python - 如何在python中使用IF语句检查两个列表的元素是不是相等?

为啥这个 if 语句不返回布尔值?