【问题标题】:Strange behavior in C when comparing integers比较整数时 C 中的奇怪行为
【发布时间】:2017-10-23 00:13:21
【问题描述】:

我的mac环境遇到这个问题,gcc版本如下图:

Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin14.5.0
Thread model: posix

代码段来自函数之一。

int someFunction(int x, int n) {
    int TMin_n = -(1 << (n-1));
    int TMax_n = (1 << (n-1)) - 1;
    int a = -2147483648;
    printf("%s\n", x == a ? "true" : "false");      // true
    printf("%s\n", x <= TMax_n ? "true" : "false"); // false
    printf("%s\n", a <= TMax_n ? "true" : "false"); // true
    printf("%d %d %d\n", TMin_n, x, TMax_n);        // -2147483648 -2147483648 2147483647
    return 0;
}

a 等于 x,但为什么它们与 TMax_n 相比有不同的行为?

更新

此代码段中的所有数据类型均为int

UPDATE2

谢谢大家。我刚刚在 ubuntu 机器上尝试了相同的代码,效果很好。它只卡在我的MAC上。似乎是编译器的问题。

【问题讨论】:

  • 什么是xTMax_n?您的示例不完整。见minimal reproducible example
  • 它不是 GCC,它是 clang(MacOS 上的默认编译器 - 好吧,现在……)。
  • 在您编辑之后,您仍然没有显示 x 的类型 - 对这种行为唯一有意义的解释是 x 是 unsigned...
  • 无法复制;至少使用 n=32,我得到 3 x true
  • 这是 100% 复制/粘贴有问题的代码吗? IE。不是 i32 someFunction(i32 x, i32 n) 带有一些自定义类型的 typedef,它们意外地未签名?

标签: c clang


【解决方案1】:

这似乎是由于表达式 (1 &lt;&lt; (n-1)) - 1 中未定义的行为,特别是在 (1 &lt;&lt; (n-1)) 中,因为 1 被视为 int(可能是 32 位),然后表达式 (1 &lt;&lt; (n-1)) 产生一个值比int 可表示的最大值大一。所以这个(中间)结果会导致有符号整数算术溢出,即 UB(参见cppreference):

当有符号整数算术运算溢出时(结果 不适合结果类型),行为未定义:它可能会换行 根据表示规则(通常为 2 补充),它可能会在某些平台上或由于编译器选项而陷入困境 (例如 GCC 和 Clang 中的 -ftrapv),或者可以通过以下方式完全优化 编译器。

因此它可能适用于某些编译器,但可能不适用于其他编译器。 但是,如果您在 bitshift 之前强制转换为 unsigned int,溢出就会消失,您会回到定义(和预期)的行为:

(int) (((unsigned)1 << (n-1)) - 1)

顺便说一句:如果您在 C 编译器的“其他编译器选项”中设置 -ftrapv,则表达式 int TMin_n = -(1 &lt;&lt; (n-1))int TMax_n = (1 &lt;&lt; (n-1)) - 1 会导致运行时异常(例如 EXC_BAD_INSTRUCTION)。

【讨论】:

  • +1 是的,您是对的,感谢您提供更好的解释和对-ftrapv 的建议(虽然这不是编译器错误,而是恕我直言的陷阱?)。我认为这是一个编译器问题,甚至没有意识到它的价值。将0x80000000 部分转换为(long long),然后将结果转换回int 也可以解决问题。 a = -2147483648 也是 UB。
  • 替代方案:使用 1u? int max = (1U &lt;&lt; (n-1)) - 1 对我来说看起来好多了。有什么明确演员的理由吗?
【解决方案2】:

我使用online clang compiler 尝试了您的 sn-p,并从问题中得到了结果。我假设代码(包括函数调用)被完全内联,然后编译器优化掉它发现的所有比较常量。

有问题的行似乎是:

int TMax_n = (1 << (n-1)) - 1;

如果我将(1 &lt;&lt; (n-1)) 明确转换为unsigned,问题就会消失:

int TMax_n = (int) ((unsigned)(1 << (n-1)) - 1);

【讨论】:

  • 非常有趣 - 但在我看来这真的像一个编译器错误!
  • @Aconcagua:实际上不是编译器错误,而是 UB。请参阅下面的答案。
  • @StephanLechner 非常正确 - 完全监督了这一点。你的好眼光...如果它真的是一个错误,会破坏我迄今为止对clang的良好印象......
猜你喜欢
  • 1970-01-01
  • 2012-04-17
  • 1970-01-01
  • 2018-06-02
  • 2015-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多