【问题标题】:Branch not taken on Linux [duplicate]在 Linux 上未采用分支 [重复]
【发布时间】:2020-01-15 01:57:45
【问题描述】:

以下代码在 Windows 上按预期工作,但是当使用 Clang 6.0 构建并在 Ubuntu 服务器上运行时,它不起作用。 CurAnimIndex 是一个 int32 值,值为 2147483647(最大 int)。我希望它进入分支,因为增量后 CurAnimIndex 的值应该是负数,但事实并非如此。

CurAnimIndex++;
if (CurAnimIndex >= AnimSelectorDatas.Num() || CurAnimIndex < 0)
{
    CurAnimIndex = 0;
}


0x000000000411a12f  mov    0x0(%r13),%eax 
0x000000000411a133  lea    0x1(%rax),%ecx 
0x000000000411a136  movslq 0x10(%r13),%r15 
0x000000000411a13a  xor    %ebp,%ebp 
0x000000000411a13c  cmp    %r15d,%ecx 
0x000000000411a13f  cmovge %ebp,%ecx 
0x000000000411a142  cmp    $0xffffffff,%eax 
0x000000000411a145  cmovl  %ebp,%ecx 
0x000000000411a148  mov    %ecx,0x0(%r13) 
0x000000000411a14c  mov    0x8(%r13),%r12 enter code here

【问题讨论】:

标签: c++ linux c++14 clang++


【解决方案1】:
CurAnimIndex++

CurAnimIndex 是一个 int32 值,值为 2147483647(最大 int)。我希望它进入分支,因为增量后 CurAnimIndex 的值应该是负数

2147483647 是一个正数。为什么你会期望增加一个正数会产生一个负数?这在普通算术中不会发生。编译器知道这一点并根据该知识进行优化。如果CurAnimIndex 的初始值已被证明至少为-1,则检查CurAnimIndex &lt; 0 始终为假,可以优化掉。

也许您的期望与操作溢出最大可表示值的事实有关。这种期望是错误的,因为不能保证签名溢出具有这种行为。事实上,签名溢出不能保证有任何特定的行为。程序的行为未定义。

这样做的正确方法是首先检查数字是否等于最大可表示值,如果不等于则仅增加。

【讨论】:

  • 我认为“普通算术”与此无关。编译器对此进行了优化,因为标准规定整数环绕是未定义的行为,因此编译器可以做它该死的任何事情。没有什么“正常”的。在 2 的补码中具有固定位数的二进制加法通常会回绕。
  • @hyde 普通算术可以消除模算术的歧义,在模块化算术中,两个数字相加的结果小于操作数是完全正常的。 C++ 中的带符号加法不是模块化的,因此澄清了普通算术中可以预期的内容。
  • 我的意思是,在问题的情况下增加 2147483647 并不会产生您在正常算术中所期望的结果。
  • @hyde specifically does not yield what you'd expect in normal arithmetic可以产生您在正常算术中所期望的结果。它还可以产生 OP 预期的结果。或者两者都不产生。对于所有没有未定义行为的操作,有符号算术具有正常结果。因为编译器可以假设没有 UB,所以它可以假设结果是正常的,无论是否正常(后者总是 UB)。
  • 结果是“未定义的行为”,对我来说绝对不是算术,甚至不是数学,更普遍的是哲学。顺便说一句,看看汇编代码,CurAnimIndex 确实会在溢出之后为负(如果没有完成 0 赋值),由于 UB,该值只是不用于比较。 (免责声明,我的组件有点生锈了。)
猜你喜欢
  • 2012-10-14
  • 2011-11-04
  • 2016-01-15
  • 2015-12-11
  • 1970-01-01
  • 2019-07-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-14
相关资源
最近更新 更多