为啥这个结合赋值和相等检查的 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,它cout
的i
为1
。如果i
在if语句中赋值为1
,为什么i == 0
返回true
?
【问题讨论】:
伙计们,这不是一个错字问题。 OP 想知道为什么使用此代码输入 if 语句,因为i
设置为 1
。
还是赋值1 && i == 0
的结果?
给初学者的建议:他们不应该使用这种“高级”的语言结构。只需单独分配变量。这也将避免序列点可能出现的问题。实际代码中的这种代码通常也会看起来很糟糕。
这肯定是面试题
【参考方案1】:
这与operator precedence 有关。
if (i = 1 && i == 0)
不是
if ((i = 1) && (i == 0))
因为&&
和==
的优先级都高于=
。它真正起作用的是
if (i = (1 && (i == 0)))
将1 && (i == 0)
的结果分配给i
。所以,如果i
以0
开头,那么i == 0
就是true
,所以1 && 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 太苛刻了,考虑到这可以正确回答问题,尽管可能不是最佳的。它没有像其他答案那样扩展运算符优先级,但它确实在代码的上下文中说明了足够多的内容,因此任何认为=
出现在&&
之前的人都可以看到这个问题。另外,是的,扩展是无关紧要的,但我认为这并不重要。我无法相信如此微小的差异会导致人们以 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
在&&
被应用之前被评估,然后你说i
被设置为&&
操作符的结果。以上是关于为啥这个结合赋值和相等检查的 if 语句返回 true?的主要内容,如果未能解决你的问题,请参考以下文章
Python中的if语句——参考Python编程从入门到实践