【问题标题】:How can both (i + 1) < ii and (i + 1) > ii both be true?(i + 1) < ii 和 (i + 1) > ii 怎么都为真?
【发布时间】:2014-03-31 12:21:05
【问题描述】:

我目前正在学习 C 程序但我遇到了一些奇怪的行为 我期望一个结果,但两个结果是这样打印的

$ ./a.out
yes1 0x80000000
yes3 0x80000000

这怎么可能?
结果看不懂。

OS : x86_64 Ubuntu Linux
C compiler : gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1

gcc -O2 weird.c

#include <stdio.h>

int main() {

    int i = 0x7fffffff;
    int ii = 0x0000000f;

    if ((i + 1) < ii)
        printf ("yes1 %#x\n", i + 1);

    if ((i + 1) == ii)
        printf ("yes2 %#x\n", i + 1);

    if ((i + 1) > ii)
        printf ("yes3 %#x\n", i + 1);

    return 0;
}

【问题讨论】:

  • 0x7fffffff + 1 = 0x80000000
  • gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2 正确(在 32 位系统上)
  • 它被删除了,但你原来有C++标签,你关心C++吗?
  • @ShafikYaghmour 你是对的!

标签: c undefined-behavior integer-overflow


【解决方案1】:

在您的情况下 (i+1) 溢出整数变量的范围。

事实上,有符号 int 变量的溢出是 ANSI 标准中未定义的行为,因此严格来说它可能会导致任何结果。您的编译器可能符合标准,但任何对计算机有良好理解的人都会认为变量会溢出为负值,这仅仅是因为计算机寄存器没有区分有符号/无符号范围。

这是 ANSI 标准在 Undefined behavior 上所说的(以及其他情况):

以下情况下的行为未定义:

算术运算无效(例如除以 0 或取模) 或产生无法在空间中表示的结果 提供(如上溢或下溢)($3.3)。

另一方面,这对无符号类型无效:

涉及无符号操作数的计算可以 永远不会溢出,因为结果不能由 得到的无符号整数类型以数字为模减少 比可以表示的最大值大一 生成的无符号整数类型。

这里也是引用部分的相关部分($3.3 表达式):

如果在计算表达式期间发生异常(即 是,如果结果不是数学定义或不可表示的), 行为未定义。

【讨论】:

  • 可以设置-fwrapv,这使得溢出完全定义为环绕。
  • 大概-fwrapv 会禁用我的回答中提到的有问题的优化。
  • 看起来您引用的不是规范的附件,虽然我在 C99 草案和 C11 草案中都找不到该措辞,但您是否引用了 C89? ANSI 含糊不清,今天我认为它是指 C11。
  • @ShafikYaghmour 我不是 100% 确定,但我认为这是 C89 的最后一稿。
  • 您也应该引用规范性文本,我很确定这是 C89,因为 3 部分是 C99 和 C11 中的术语和定义。这个thread 包含所有可用的公共草稿,因此您可以从那里提供的链接中提取 C99 和 C11 草稿。
【解决方案2】:

在您的代码i+1 中的所有情况下,都会产生有符号整数溢出,即undefined behavior,这意味着程序的行为可能无法预测。我们可以从 draft C99 standard 部分 6.5 Expressions 段落 5 中看到它是未定义的(emphasis mine):

如果在计算表达式期间出现异常情况(即,如果 结果未在数学上定义或不在其可表示值的范围内 type),行为未定义

这也是 C11 标准草案中的相同部分。

一种可能的方法是使用clangs -fsanitize=undefined 选项,这是clangs santizer suite 的一部分,我们会收到以下运行时错误:

runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'

【讨论】:

    【解决方案3】:

    我认为您遇到了一个优化器情况,编译器说对于整数,x+1&gt;y 可以更有效地计算为x&gt;=y... 不幸的是,虽然这通常是正确的,并且对于无符号的总是正确的值,这个 x+1 环绕为负的特定情况打破了假设。

    但是,由于signed int 溢出将我们带入未定义行为的领域(请参阅@Marian 的回答),这不是错误的。只是令人惊讶。

    (考虑删除我的答案,但由于它说明了两个测试如何为真,我认为值得保留。)

    【讨论】:

    • 这不是编译器错误。有符号整数溢出是未定义的行为。
    • 谢谢;适当澄清。
    • 这里发挥的具体优化是-fstrict-overflow。在-O2 下,gcc 启用-fstrict-overflow 优化,这允许它假设有符号溢出是未定义的。使用-fwrapv 会使编译器假定溢出会导致回绕。有趣的是,在 C99 规范第 3.4.3 节中,为定义未定义行为给出的示例说“未定义行为的示例是整数溢出时的行为”
    猜你喜欢
    • 2015-08-17
    • 2011-10-05
    • 2022-07-13
    • 2012-08-25
    • 1970-01-01
    • 2015-05-24
    • 2014-02-22
    • 2019-11-08
    • 2014-03-04
    相关资源
    最近更新 更多