(x+1) > x 如何评估为 0 和 1?

avm*_*avm 7 c c++ undefined-behavior

我正在学习未定义的行为,并在没有任何明确解释的情况下偶然发现了这段代码:

#include <stdio.h>
#include <limits.h>

int foo ( int x) {
    printf ("% d\n" ,  x );   //2147483647
    printf ("% d\n" ,  x+1 ); //-2147483648  overflow
    return ( x+1 ) > x ;      // 1 but How????
}

int main ( void ) {
    printf ("% d\n" ,  INT_MAX );     //2147483647
    printf ("% d\n" ,  INT_MAX+1 );   //-2147483648  overflow
    printf ("% d\n" , ( INT_MAX+1 ) > INT_MAX );  //0  makes sense, since -ve < +ve
    printf ("% d\n" ,  foo(INT_MAX) );  //1
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在 gcc 上编译时,编译器发出警告:

警告:“int”类型的表达式中的整数溢出导致“-2147483648”

因此,显然 的值为INT_MAX+1负,这就解释了为什么(INT_MAX+1) > INT_MAX计算结果为 0。

但是,为什么(或如何)对in(x+1) > x求值为1x = INT_MAXfoo(...)

dbu*_*ush 13

当程序表现出未定义的行为时,C 标准不会预测程序将做什么。程序可能会崩溃,可能会输出奇怪的结果,或者可能看起来工作正常。

事实上,编译器通常会在假设程序不包含未定义行为的情况下工作。

在这个表达式的情况下:

( x+1 ) > x 
Run Code Online (Sandbox Code Playgroud)

鉴于它x具有 type int,编译器知道有符号溢出是 UB 并且在它不会发生的假设下工作。考虑到这一点,x该表达式可能为假的地方没有值,因此编译器可以优化该表达式并将其替换为值 1。

当我在 gcc 4.8.5 下运行这个程序时,我得到以下结果-O0-O1

( x+1 ) > x 
Run Code Online (Sandbox Code Playgroud)

并以下列-O2-O3

 2147483647
-2147483648
 0
 2147483647
-2147483648
 0
Run Code Online (Sandbox Code Playgroud)

然后foo在后一种情况下查看程序集:

foo:
.LFB11:
    .file 1 "x1.c"
    .loc 1 4 0
    .cfi_startproc
.LVL0:
    pushq   %rbx                // first call to printf
    .cfi_def_cfa_offset 16
    .cfi_offset 3, -16
    .loc 1 5 0
    movl    %edi, %esi
    .loc 1 4 0
    movl    %edi, %ebx
    .loc 1 5 0
    xorl    %eax, %eax
    movl    $.LC0, %edi
.LVL1:
    call    printf
.LVL2:
    .loc 1 6 0                  // second call to printf
    leal    1(%rbx), %esi
    movl    $.LC0, %edi
    xorl    %eax, %eax
    call    printf
.LVL3:
    .loc 1 8 0                  // return value
    movl    $1, %eax
    popq    %rbx
    .cfi_def_cfa_offset 8
.LVL4:
    ret
    .cfi_endproc

Run Code Online (Sandbox Code Playgroud)

我们可以看到这正是编译器所做的:它优化了比较并始终返回 1。

这说明了编译器如何利用未定义的行为来应用各种优化。

  • @avm 注意到未定义行为的开销将是巨大的损失。按照设计,C++ 假设您知道自己在做什么。 (5认同)
  • @avm • **未定义的行为**不是编译器问题,而是程序员问题。C 和 C++ 不是保姆语言。他们假设程序员知道他们在做什么,并且永远不会对编译器撒谎,并且代码中永远不会有“未定义的行为”。在现实世界中,这会转化为大量的&lt;strike&gt;bug&lt;/strike&gt; *工作保障*。 (4认同)
  • @avm:其他语言可以并且确实做到了这一点。它会减慢代码的速度,使其始终检查溢出。C 不会做你没有要求的额外工作。 (3认同)
  • @avm 它没有检测到 UB。它只是假设它不会发生。这就是 C 语言快速的部分原因。要不要引入UB就看程序员了。并且 `INT_MAX+1 &gt; INT_MAX` *是* UB。您获得预期结果的事实不会改变这一点。 (3认同)