为什么这个循环产生“警告:迭代3u调用未定义的行为”并输出4行以上? [英] Why does this loop produce "warning: iteration 3u invokes undefined behavior" and output more than 4 lines?

查看:5645
本文介绍了为什么这个循环产生“警告:迭代3u调用未定义的行为”并输出4行以上?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

正在编译:

  #include< iostream> 

int main()
{
for(int i = 0; i <4; ++ i)
std :: cout< i * 1000000000 < std :: endl;
}

gcc 以下警告:

 警告:iteration 3u调用未定义的行为[-Waggressive-循环优化] 
std :: cout < i * 1000000000 < std :: endl;
^



我知道有一个有符号整数溢出。



我不能得到的是为什么 i 值被溢出​​操作破坏了。



我阅读了的答案为什么x86上的整数溢出与GCC导致无限循环?,但我仍然不清楚为什么这发生 - 我得到的未定义意味着任何可能发生但是此特定行为的根本原因是什么?



在线: http://ideone.com/dMrRKR



编译器: gcc(4.8)

解决方案

签名的整数溢出(严格来说,没有无符号整数溢出这样的东西)意味着。这意味着任何可能发生,并讨论为什么它发生在C ++的规则下是没有意义的。



C ++ 11草案N3337:§5.4: sup> 1


如果在表达式求值过程中,结果不是数学定义的, b $ b它的类型的可表示的值,行为是未定义的。 [注意:大多数现有的C ++实现
忽略整数溢出。处理除以零,使用零除数形成余数,并且所有
浮点异常在机器中不同,并且通常可以通过库函数调整。 -end note]


您的代码编译为 g ++ -O3 即使没有 -Wall

  a.cpp: main()':
a.cpp:11:18:warning:iteration 3u调用未定义的行为[-Waggressive-loop-optimizations]
std :: cout< i * 1000000000 < std :: endl;
^
a.cpp:9:2:note:包含循环
for(int i = 0; i <4; ++ i)
^

我们可以分析程序正在做什么的唯一方法是读取生成的汇编代码。



这是完整的装配列表:

  .filea.cpp
.section .text $ _ZNKSt5ctypeIcE8do_widenEc,x
.linkonce discard
.align 2
LCOLDB0:
LHOTB0:
.align 2
。 p2align 4,,15
.globl __ZNKSt5ctypeIcE8do_widenEc
.def __ZNKSt5ctypeIcE8do_widenEc; .scl 2; .type 32; .endef
__ZNKSt5ctypeIcE8do_widenEc:
LFB860:
.cfi_startproc
movzbl 4(%esp),%eax
ret $ 4
.cfi_endproc
LFE860 :
LCOLDE0:
LHOTE0:
.section .text.unlikely,x
LCOLDB1:
.text
LHOTB1:
。 p2align 4,,15
.def ___tcf_0; .sc13; .type 32; .endef
___tcf_0:
LFB1091:
.cfi_startproc
movl $ __ ZStL8__ioinit,%ecx
jmp __ZNSt8ios_base4InitD1Ev
.cfi_endproc
LFE1091:
.section .text.unlikely,x
LCOLDE1:
.text
LHOTE1:
.def ___main; .sc12; .type 32; .endef
.section .text.unlikely,x
LCOLDB2:
.section .text.startup,x
LHOTB2:
.p2align 4, ,15
.globl _main
.def _main; .scl 2; type 32; .endef
_main:
LFB1084:
.cfi_startproc
leal 4(%esp),%ecx
.cfi_def_cfa 1,0
andl $ -16 ,%esp
pushl -4(%ecx)
pushl%ebp
.cfi_escape 0x10,0x5,0x2,0x75,0
movl%esp,%ebp
pushl%edi
pushl%esi
pushl%ebx
pushl%ecx
.cfi_escape 0xf,0x3,0x75,0x70,0x6
.cfi_escape 0x10,0x7,0x2 ,0x75,0x7c
.cfi_escape 0x10,0x6,0x2,0x75,0x78
.cfi_escape 0x10,0x3,0x2,0x75,0x74
xorl%edi,%edi
subl $ 24 ,%esp
call ___main
L4:
movl%edi,(%esp)
movl $ __ ZSt4cout,%ecx
call __ZNSolsEi
movl%eax ,%esi
movl(%eax),%eax
subl $ 4,%esp
movl -12(%eax),%eax
movl 124(%esi,%eax ),%ebx
testl%ebx,%ebx
je L15
cmpb $ 0,28(%ebx)
je L5
movsbl 39(%ebx) eax
L6:
movl%esi,%ecx
movl%eax,(%esp)
addl $ 1000000000,%edi
call __ZNSo3putEc
subl $ 4 ,%esp
movl%eax,%ecx
call __ZNSo5flushEv
jmp L4
.p2align 4,and 10
L5:
movl%ebx,% ecx
call __ZNKSt5ctypeIcE13_M_widen_initEv
movl(%ebx),%eax
movl 24(%eax),%edx
movl $ 10,%eax
cmpl $ __ ZNKSt5ctypeIcE8do_widenEc, edx
je L6
movl $ 10,(%esp)
movl%ebx,%ecx
call *%edx
movsbl%al,%eax
pushl%edx
jmp L6
L15:
call __ZSt16__throw_bad_castv
.cfi_endproc
LFE1084:
.section .text.unlikely,x
LCOLDE2:
.section .text.startup,x
LHOTE2:
.section .text.unlikely,x
LCOLDB3:
.section。 text.startup,x
LHOTB3:
.p2align 4,and 15
.def __GLOBAL__sub_I_main; .sc13; .type 32; .endef
__GLOBAL__sub_I_main:
LFB1092:
.cfi_startproc
subl $ 28,%esp
.cfi_def_cfa_offset 32​​
movl $ __ ZStL8__ioinit,%ecx
调用__ZNSt8ios_base4InitC1Ev
movl $ ___ tcf_0,(%esp)
调用_atexit
addl $ 28,%esp
.cfi_def_cfa_offset 4
ret
.cfi_endproc
LFE1092:
.section .text.unlikely,x
LCOLDE3:
.section .text.startup,x
LHOTE3:
.section。 ctors,w
.align 4
.long __GLOBAL__sub_I_main
.lcomm __ZStL8__ioinit,1,1
.identGCC:(i686-posix-dwarf-rev1, MinGW-W64 project)4.9.0
.def __ZNSt8ios_base4InitD1Ev; .scl 2; .type 32; .endef
.def __ZNSolsEi; .scl 2; .type 32; .endef
.def __ZNSo3putEc; .scl 2; .type 32; .endef
.def __ZNSo5flushEv; .scl 2; .type 32; .endef
.def __ZNKSt5ctypeIcE13_M_widen_initEv; .scl 2; .type 32; .endef
.def __ZSt16__throw_bad_castv; .scl 2; .type 32; .endef
.def __ZNSt8ios_base4InitC1Ev; .scl 2; .type 32; .endef
.def _atexit; .scl 2; .type 32; .endef



我几乎不能读取程序集,但是我可以看到 addl $ 1000000000,%edi 线。
生成的代码看起来更像

  for(int i = 0; / *没什么, * /; i + = 1000000000)
std :: cout< i<< std :: endl;

@TC的此评论:



<

我怀疑它是这样的:(1)因为每个迭代 i 任何大于2的值都有未定义的行为 - >可以假设 i <= 2 用于优化目的 - >(3)循环条件总是真 - >(4)它被优化为无限循环。 p>

让我想法将OP的代码的汇编代码与下面代码的汇编代码进行比较,没有未定义的行为。

  #include< iostream> 

int main()
{
//改变终止条件
for(int i = 0; i <3; ++ i)
std :: cout<< i * 1000000000 < std :: endl;
}

事实上,正确的代码有终止条件。

 ; ... snip ... 
L6:
mov ecx,edi
mov DWORD PTR [esp],eax
add esi,1000000000
call __ZNSo3putEc
sub esp,4
mov ecx,eax
call __ZNSo5flushEv
cmp esi,-1294967296 //这里是
jne L7
lea esp,[ebp-16 ]
xor eax,eax
pop ecx
; ... snb ... o




OMG,完全不明显!这不公平!


处理它,你写了buggy代码,你应该感到不舒服。承担后果。



...或者,正确使用更好的诊断和更好的调试工具 - 这是他们的目的:




  • 启用所有警告




    • Wall 是gcc选项,启用所有有用的警告,没有误报。这是您应该总是使用的最低限度。

    • gcc有许多其他警告选项,但是,它们没有启用 -Wall ,因为他们可能对误报警告

    • Visual C ++不幸的是落后于提供有用警告的能力。

    • 使用调试标志进行调试




      • 用于整数溢出 -ftrapv 在溢出时陷阱程序,

      • Clang编译器优秀的: -fcatch-undefined-behavior 捕获了大量未定义行为的实例(注意:很多!=所有他们




    p>我有一个意大利面条的程序不是由我写的,需要明天发货!帮助!!!!!! 111oneone


    使用gcc的 -fwrapv p>


    此选项指示编译器假定加法,减法和乘法的有符号算术溢出使用二进制补码表示。


    1 - 此规则不适用于无符号整数溢出,如§3.9.1.4所述


    无符号整数,声明为无符号,应遵守算术模2的算法 n 其中n是数字
    在整数的特定大小的值表示中的位。


    UINT_MAX + 1 的结果在数学上通过算术模2定义 n


    Compiling this:

    #include <iostream>
    
    int main()
    {
        for (int i = 0; i < 4; ++i)
            std::cout << i*1000000000 << std::endl;
    }
    

    and gcc produces the following warning:

    warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
       std::cout << i*1000000000 << std::endl;
                      ^
    

    I understand there is a signed integer overflow.

    What I cannot get is why i value is broken by that overflow operation?

    I've read the answers to Why does integer overflow on x86 with GCC cause an infinite loop?, but I'm still not clear on why this happens - I get that "undefined" means "anything can happen", but what's the underlying cause of this specific behavior?

    Online: http://ideone.com/dMrRKR

    Compiler: gcc (4.8)

    解决方案

    Signed integer overflow (as strictly speaking, there is no such thing as "unsigned integer overflow") means undefined behaviour. And this means anything can happen, and discussing why does it happen under the rules of C++ doesn't make sense.

    C++11 draft N3337: §5.4:1

    If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined. [ Note: most existing implementations of C++ ignore integer overflows. Treatment of division by zero, forming a remainder using a zero divisor, and all floating point exceptions vary among machines, and is usually adjustable by a library function. —end note ]

    Your code compiled with g++ -O3 emits warning (even without -Wall)

    a.cpp: In function 'int main()':
    a.cpp:11:18: warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
       std::cout << i*1000000000 << std::endl;
                      ^
    a.cpp:9:2: note: containing loop
      for (int i = 0; i < 4; ++i)
      ^
    

    The only way we can analyze what the program is doing, is by reading the generated assembly code.

    Here is the full assembly listing:

        .file   "a.cpp"
        .section    .text$_ZNKSt5ctypeIcE8do_widenEc,"x"
        .linkonce discard
        .align 2
    LCOLDB0:
    LHOTB0:
        .align 2
        .p2align 4,,15
        .globl  __ZNKSt5ctypeIcE8do_widenEc
        .def    __ZNKSt5ctypeIcE8do_widenEc;    .scl    2;  .type   32; .endef
    __ZNKSt5ctypeIcE8do_widenEc:
    LFB860:
        .cfi_startproc
        movzbl  4(%esp), %eax
        ret $4
        .cfi_endproc
    LFE860:
    LCOLDE0:
    LHOTE0:
        .section    .text.unlikely,"x"
    LCOLDB1:
        .text
    LHOTB1:
        .p2align 4,,15
        .def    ___tcf_0;   .scl    3;  .type   32; .endef
    ___tcf_0:
    LFB1091:
        .cfi_startproc
        movl    $__ZStL8__ioinit, %ecx
        jmp __ZNSt8ios_base4InitD1Ev
        .cfi_endproc
    LFE1091:
        .section    .text.unlikely,"x"
    LCOLDE1:
        .text
    LHOTE1:
        .def    ___main;    .scl    2;  .type   32; .endef
        .section    .text.unlikely,"x"
    LCOLDB2:
        .section    .text.startup,"x"
    LHOTB2:
        .p2align 4,,15
        .globl  _main
        .def    _main;  .scl    2;  .type   32; .endef
    _main:
    LFB1084:
        .cfi_startproc
        leal    4(%esp), %ecx
        .cfi_def_cfa 1, 0
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        .cfi_escape 0x10,0x5,0x2,0x75,0
        movl    %esp, %ebp
        pushl   %edi
        pushl   %esi
        pushl   %ebx
        pushl   %ecx
        .cfi_escape 0xf,0x3,0x75,0x70,0x6
        .cfi_escape 0x10,0x7,0x2,0x75,0x7c
        .cfi_escape 0x10,0x6,0x2,0x75,0x78
        .cfi_escape 0x10,0x3,0x2,0x75,0x74
        xorl    %edi, %edi
        subl    $24, %esp
        call    ___main
    L4:
        movl    %edi, (%esp)
        movl    $__ZSt4cout, %ecx
        call    __ZNSolsEi
        movl    %eax, %esi
        movl    (%eax), %eax
        subl    $4, %esp
        movl    -12(%eax), %eax
        movl    124(%esi,%eax), %ebx
        testl   %ebx, %ebx
        je  L15
        cmpb    $0, 28(%ebx)
        je  L5
        movsbl  39(%ebx), %eax
    L6:
        movl    %esi, %ecx
        movl    %eax, (%esp)
        addl    $1000000000, %edi
        call    __ZNSo3putEc
        subl    $4, %esp
        movl    %eax, %ecx
        call    __ZNSo5flushEv
        jmp L4
        .p2align 4,,10
    L5:
        movl    %ebx, %ecx
        call    __ZNKSt5ctypeIcE13_M_widen_initEv
        movl    (%ebx), %eax
        movl    24(%eax), %edx
        movl    $10, %eax
        cmpl    $__ZNKSt5ctypeIcE8do_widenEc, %edx
        je  L6
        movl    $10, (%esp)
        movl    %ebx, %ecx
        call    *%edx
        movsbl  %al, %eax
        pushl   %edx
        jmp L6
    L15:
        call    __ZSt16__throw_bad_castv
        .cfi_endproc
    LFE1084:
        .section    .text.unlikely,"x"
    LCOLDE2:
        .section    .text.startup,"x"
    LHOTE2:
        .section    .text.unlikely,"x"
    LCOLDB3:
        .section    .text.startup,"x"
    LHOTB3:
        .p2align 4,,15
        .def    __GLOBAL__sub_I_main;   .scl    3;  .type   32; .endef
    __GLOBAL__sub_I_main:
    LFB1092:
        .cfi_startproc
        subl    $28, %esp
        .cfi_def_cfa_offset 32
        movl    $__ZStL8__ioinit, %ecx
        call    __ZNSt8ios_base4InitC1Ev
        movl    $___tcf_0, (%esp)
        call    _atexit
        addl    $28, %esp
        .cfi_def_cfa_offset 4
        ret
        .cfi_endproc
    LFE1092:
        .section    .text.unlikely,"x"
    LCOLDE3:
        .section    .text.startup,"x"
    LHOTE3:
        .section    .ctors,"w"
        .align 4
        .long   __GLOBAL__sub_I_main
    .lcomm __ZStL8__ioinit,1,1
        .ident  "GCC: (i686-posix-dwarf-rev1, Built by MinGW-W64 project) 4.9.0"
        .def    __ZNSt8ios_base4InitD1Ev;   .scl    2;  .type   32; .endef
        .def    __ZNSolsEi; .scl    2;  .type   32; .endef
        .def    __ZNSo3putEc;   .scl    2;  .type   32; .endef
        .def    __ZNSo5flushEv; .scl    2;  .type   32; .endef
        .def    __ZNKSt5ctypeIcE13_M_widen_initEv;  .scl    2;  .type   32; .endef
        .def    __ZSt16__throw_bad_castv;   .scl    2;  .type   32; .endef
        .def    __ZNSt8ios_base4InitC1Ev;   .scl    2;  .type   32; .endef
        .def    _atexit;    .scl    2;  .type   32; .endef
    

    I can barely even read assembly, but even I can see the addl $1000000000, %edi line. The resulting code looks more like

    for(int i = 0; /* nothing, that is - infinite loop */; i += 1000000000)
        std::cout << i << std::endl;
    

    This comment of @T.C.:

    I suspect that it's something like: (1) because every iteration with i of any value larger than 2 has undefined behavior -> (2) we can assume that i <= 2 for optimization purposes -> (3) the loop condition is always true -> (4) it's optimized away into an infinite loop.

    gave me idea to compare the assembly code of the OP's code to the assembly code of the following code, with no undefined behaviour.

    #include <iostream>
    
    int main()
    {
        // changed the termination condition
        for (int i = 0; i < 3; ++i)
            std::cout << i*1000000000 << std::endl;
    }
    

    And, in fact, the correct code has termination condition.

        ; ...snip...
    L6:
        mov ecx, edi
        mov DWORD PTR [esp], eax
        add esi, 1000000000
        call    __ZNSo3putEc
        sub esp, 4
        mov ecx, eax
        call    __ZNSo5flushEv
        cmp esi, -1294967296 // here it is
        jne L7
        lea esp, [ebp-16]
        xor eax, eax
        pop ecx
        ; ...snip...
    

    OMG, that's completely not obvious! It's not fair! I demand trial by fire!

    Deal with it, you wrote the buggy code and you should feel bad. Bear the consequences.

    ...or, alternatively, make proper use of better diagnostics and better debugging tools - that's what they are for:

    • enable all warnings

      • -Wall is the gcc option that enables all useful warnings with no false positives. This is a bare minimum that you should always use.
      • gcc has many other warning options, however, they are not enabled with -Wall as they may warn on false positives
      • Visual C++ unfortunately is lagging behind with the ability to give useful warnings. At least the IDE enables some by default.
    • use debug flags for debugging

      • for integer overflow -ftrapv traps the program on overflow,
      • Clang compiler is excellent for this: -fcatch-undefined-behavior catches a lot of instances of undefined behaviour (note: "a lot of" != "all of them")

    I have a spaghetti mess of a program not written by me that needs to be shipped tomorrow! HELP!!!!!!111oneone

    Use gcc's -fwrapv

    This option instructs the compiler to assume that signed arithmetic overflow of addition, subtraction and multiplication wraps around using twos-complement representation.

    1 - this rule does not apply to "unsigned integer overflow", as §3.9.1.4 says that

    Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer.

    and e.g. result of UINT_MAX + 1 is mathematically defined - by the rules of arithmetic modulo 2n

    这篇关于为什么这个循环产生“警告:迭代3u调用未定义的行为”并输出4行以上?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆