简体   繁体   English

为什么这个循环会产生“警告:迭代 3u 调用未定义行为”并输出超过 4 行?

[英]Why does this loop produce “warning: iteration 3u invokes undefined behavior” and output more than 4 lines?

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:gcc产生以下警告:

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值被溢出操作破坏了?

I've read the answers to Why does integer overflow on x86 with GCC cause an infinite loop?我已经阅读了为什么带有 GCC 的 x86 上的整数溢出会导致无限循环的答案 , 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在线: http : //ideone.com/dMrRKR

Compiler: gcc (4.8)编译器: 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++ 规则下发生是没有意义的。

C++11 draft N3337: §5.4: 1 C++11 草案 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. [注意:大多数现有的 C++ 实现都忽略整数溢出。 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 )您使用g++ -O3编译的代码会发出警告(即使没有-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.我什至几乎看不懂汇编,但即使我能看到addl $1000000000, %edi行。 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 @TC: @TC 的评论:

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

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.给了我将 OP 代码的汇编代码与以下代码的汇编代码进行比较的想法,没有未定义的行为。

#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...

Unfortunately this is the consequences of writing buggy code.不幸的是,这是编写错误代码的后果。

Fortunately you can make 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. -Wall是 gcc 选项,它启用所有有用的警告而没有误报。 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 gcc 有许多其他警告选项,但是,它们没有通过-Wall启用,因为它们可能会在误报时发出警告

  • Visual C++ unfortunately is lagging behind with the ability to give useful warnings.不幸的是,Visual C++ 在提供有用警告的能力方面落后了。 At least the IDE enables some by default.至少 IDE 默认启用了一些。

  • use debug flags for debugging使用调试标志进行调试

    • for integer overflow -ftrapv traps the program on overflow,对于整数溢出-ftrapv在溢出时捕获程序,
    • Clang compiler is excellent for this: -fcatch-undefined-behavior catches a lot of instances of undefined behaviour (note: "a lot of" != "all of them" ) Clang 编译器在这方面非常出色: -fcatch-undefined-behavior捕获了很多未定义行为的实例(注意: "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帮助!!!!!!111oneone

Use gcc's -fwrapv使用 gcc 的-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 1 - 此规则不适用于“无符号整数溢出”,如第 3.9.1.4 节所述

Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2 n where n is the number of bits in the value representation of that particular size of integer.声明为无符号的无符号整数应遵守算术模 2 n的法则,其中 n 是该特定整数大小的值表示中的位数。

and eg result of UINT_MAX + 1 is mathematically defined - by the rules of arithmetic modulo 2 n并且例如UINT_MAX + 1结果是数学定义的 - 由算术模 2 n的规则

Short answer, gcc specifically has documented this problem, we can see that in the gcc 4.8 release notes which says ( emphasis mine going forward ):简短的回答, gcc专门记录了这个问题,我们可以在gcc 4.8 发行说明中看到它说(强调我的未来):

GCC now uses a more aggressive analysis to derive an upper bound for the number of iterations of loops using constraints imposed by language standards . GCC 现在使用更积极的分析来使用语言标准强加的约束来推导出循环迭代次数的上限 This may cause non-conforming programs to no longer work as expected, such as SPEC CPU 2006 464.h264ref and 416.gamess.这可能会导致不符合要求的程序不再按预期工作,例如 SPEC CPU 2006 464.h264ref 和 416.gamess。 A new option, -fno-aggressive-loop-optimizations, was added to disable this aggressive analysis.添加了一个新选项 -fno-aggressive-loop-optimizations 以禁用此积极分析。 In some loops that have known constant number of iterations, but undefined behavior is known to occur in the loop before reaching or during the last iteration, GCC will warn about the undefined behavior in the loop instead of deriving lower upper bound of the number of iterations for the loop.在某些已知迭代次数恒定的循环中,但已知在到达之前或最后一次迭代期间在循环中发生了未定义的行为,GCC 将警告循环中的未定义行为,而不是推导出迭代次数的下上限为循环。 The warning can be disabled with -Wno-aggressive-loop-optimizations.可以使用 -Wno-aggressive-loop-optimizations 禁用警告。

and indeed if we use -fno-aggressive-loop-optimizations the infinite loop behavior should cease and it does in all the cases I have tested.事实上,如果我们使用-fno-aggressive-loop-optimizations无限循环行为应该停止,并且在我测试过的所有情况下都是如此。

The long answer starts with knowing that signed integer overflow is undefined behavior by looking at the draft C++ standard section 5 Expressions paragraph 4 which says:通过查看 C++ 标准草案第5表达式4段,知道有符号整数溢出是未定义的行为,答案很长,它说:

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 .如果在对表达式求值期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为为 undefined [ Note: most existing implementations of C++ ignore integer overflows. [ 注意:大多数现有的 C++ 实现都忽略整数溢出。 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 ——尾注

We know that the standard says undefined behavior is unpredictable from the note that come with the definition which says:我们知道标准说未定义的行为是不可预测的,根据定义附带的注释说:

[ Note: Undefined behavior may be expected when this International Standard omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data. [注意:当本国际标准省略任何明确的行为定义或程序使用错误的构造或错误的数据时,可能会出现未定义的行为。 Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results , to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).允许的未定义行为的范围从完全忽略情况并产生不可预测的结果,在翻译或程序执行期间以环境特征的文件化方式(有或没有发布诊断消息),到终止翻译或执行(通过发布诊断消息)。 Many erroneous program constructs do not engender undefined behavior;许多错误的程序结构不会产生未定义的行为; they are required to be diagnosed.他们需要被诊断。 —end note ] ——尾注]

But what in the world can the gcc optimizer be doing to turn this into an infinite loop?但是gcc优化器到底能做什么来把它变成一个无限循环呢? It sounds completely wacky.这听起来很古怪。 But thankfully gcc gives us a clue to figuring it out in the warning:但幸运的是gcc在警告中给了我们一个线索来解决它:

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

The clue is the Waggressive-loop-optimizations , what does that mean?线索是Waggressive-loop-optimizations ,这是什么意思? Fortunately for us this is not the first time this optimization has broken code in this way and we are lucky because John Regehr has documented a case in the article GCC pre-4.8 Breaks Broken SPEC 2006 Benchmarks which shows the following code:对我们来说幸运的是,这不是这种优化第一次以这种方式破坏代码,我们很幸运,因为John RegehrGCC pre-4.8 Breaks Broken SPEC 2006 Benchmarks文章中记录了一个案例,其中显示了以下代码:

int d[16];

int SATD (void)
{
  int satd = 0, dd, k;
  for (dd=d[k=0]; k<16; dd=d[++k]) {
    satd += (dd < 0 ? -dd : dd);
  }
  return satd;
}

the article says:文章说:

The undefined behavior is accessing d[16] just before exiting the loop.未定义的行为是在退出循环之前访问 d[16]。 In C99 it is legal to create a pointer to an element one position past the end of the array, but that pointer must not be dereferenced.在 C99 中,创建一个指向数组末尾后一个位置的元素的指针是合法的,但该指针不能被取消引用。

and later on says:后来说:

In detail, here is what's going on.详细来说,这是发生了什么。 AC compiler, upon seeing d[++k], is permitted to assume that the incremented value of k is within the array bounds, since otherwise undefined behavior occurs. AC 编译器在看到 d[++k] 时,可以假设 k 的递增值在数组边界内,否则会发生未定义的行为。 For the code here, GCC can infer that k is in the range 0..15.对于这里的代码, GCC 可以推断出 k 的范围是 0..15。 A bit later, when GCC sees k<16, it says to itself: “Aha– that expression is always true, so we have an infinite loop.”稍后,当 GCC 看到 k<16 时,它对自己说:“啊哈——这个表达式总是正确的,所以我们有一个无限循环。” The situation here, where the compiler uses the assumption of well-definedness to infer a useful dataflow fact,这里的情况,编译器使用定义良好的假设来推断有用的数据流事实,

So what the compiler must be doing in some cases is assuming since signed integer overflow is undefined behavior then i must always be less than 4 and thus we have an infinite loop.所以编译器在某些情况下必须做的是假设因为有符号整数溢出是未定义的行为,那么i必须始终小于4 ,因此我们有一个无限循环。

He explains this is very similar to the infamous Linux kernel null pointer check removal where in seeing this code:他解释说,这与臭名昭著的Linux 内核空指针检查删除非常相似,其中看到以下代码:

struct foo *s = ...;
int x = s->f;
if (!s) return ERROR;

gcc inferred that since s was deferenced in s->f; gcc推断,因为ss->f;被尊重s->f; and since dereferencing a null pointer is undefined behavior then s must not be null and therefore optimizes away the if (!s) check on the next line.并且由于取消引用空指针是未定义的行为,因此s不能为空,因此优化了下一行的if (!s)检查。

The lesson here is that modern optimizers are very aggressive about exploiting undefined behavior and most likely will only get more aggressive.这里的教训是,现代优化器非常热衷于利用未定义的行为,而且很可能只会变得更加激进。 Clearly with just a few examples we can see the optimizer does things that seem completely unreasonable to a programmer but in retrospect from the optimizers perspective make sense.很明显,通过几个例子,我们可以看到优化器做了一些对程序员来说完全不合理的事情,但从优化器的角度回想起来是有道理的。

tl;dr The code generates a test that integer + positive integer == negative integer . tl;dr代码生成一个测试,整数+正整数==负整数 Usually the optimizer does not optimize this out, but in the specific case of std::endl being used next, the compiler does optimize this test out.通常优化器不会优化这个,但是在接下来使用std::endl的特定情况下,编译器会优化这个测试。 I haven't figured out what's special about endl yet.我还没有弄清楚endl有什么特别之处。


From the assembly code at -O1 and higher levels, it is clear that gcc refactors the loop to:从 -O1 和更高级别的汇编代码,很明显 gcc 将循环重构为:

i = 0;
do {
    cout << i << endl;
    i += NUMBER;
} 
while (i != NUMBER * 4)

The biggest value that works correctly is 715827882 , ie floor( INT_MAX/3 ).正常工作的最大值是715827882 ,即 floor( INT_MAX/3 )。 The assembly snippet at -O1 is:在组装片断-O1是:

L4:
movsbl  %al, %eax
movl    %eax, 4(%esp)
movl    $__ZSt4cout, (%esp)
call    __ZNSo3putEc
movl    %eax, (%esp)
call    __ZNSo5flushEv
addl    $715827882, %esi
cmpl    $-1431655768, %esi
jne L6
    // fallthrough to "return" code

Note, the -1431655768 is 4 * 715827882 in 2's complement.请注意, -1431655768是 2 的补码中的4 * 715827882

Hitting -O2 optimizes that to the following:点击-O2优化为以下内容:

L4:
movsbl  %al, %eax
addl    $715827882, %esi
movl    %eax, 4(%esp)
movl    $__ZSt4cout, (%esp)
call    __ZNSo3putEc
movl    %eax, (%esp)
call    __ZNSo5flushEv
cmpl    $-1431655768, %esi
jne L6
leal    -8(%ebp), %esp
jne L6 
   // fallthrough to "return" code

So the optimization that has been made is merely that the addl was moved higher up.所以所做的优化只是将addl向上移动。

If we recompile with 715827883 instead then the -O1 version is identical apart from the changed number and test value.如果我们715827883重新编译,那么除了更改的数字和测试值之外, 715827883版本是相同的。 However, -O2 then makes a change:但是,-O2 然后进行了更改:

L4:
movsbl  %al, %eax
addl    $715827883, %esi
movl    %eax, 4(%esp)
movl    $__ZSt4cout, (%esp)
call    __ZNSo3putEc
movl    %eax, (%esp)
call    __ZNSo5flushEv
jmp L2

Where there was cmpl $-1431655764, %esi at -O1 , that line has been removed for -O2 .有人的地方就有cmpl $-1431655764, %esi-O1 ,该行已经为去除-O2 The optimizer must have decided that adding 715827883 to %esi can never equal -1431655764 .优化器必须决定将715827883添加到%esi永远不能等于-1431655764

This is pretty puzzling.这很令人费解。 Adding that to INT_MIN+1 does generate the expected result, so the optimizer must have decided that %esi can never be INT_MIN+1 and I'm not sure why it would decide that.将其添加到INT_MIN+1确实会生成预期的结果,因此优化器肯定已经决定%esi永远不会是INT_MIN+1并且我不确定为什么会这样决定。

In the working example it seems it'd be equally valid to conclude that adding 715827882 to a number cannot equal INT_MIN + 715827882 - 2 !在工作示例中,得出将715827882添加到数字不能等于INT_MIN + 715827882 - 2结论似乎同样有效! (this is only possible if wraparound does actually occur), yet it does not optimize the line out in that example. (这只有在实际发生回绕时才有可能),但它并没有优化该示例中的线路。


The code I was using is:我使用的代码是:

#include <iostream>
#include <cstdio>

int main()
{
    for (int i = 0; i < 4; ++i)
    {
        //volatile int j = i*715827883;
        volatile int j = i*715827882;
        printf("%d\n", j);

        std::endl(std::cout);
    }
}

If the std::endl(std::cout) is removed then the optimization no longer occurs.如果std::endl(std::cout)被删除,则优化不再发生。 In fact replacing it with std::cout.put('\\n'); std::flush(std::cout);实际上用std::cout.put('\\n'); std::flush(std::cout);替换它std::cout.put('\\n'); std::flush(std::cout); std::cout.put('\\n'); std::flush(std::cout); also causes the optimization to not happen, even though std::endl is inlined.也会导致优化不会发生,即使std::endl是内联的。

The inlining of std::endl seems to affect the earlier part of the loop structure (which I don't quite understand what it is doing but I'll post it here in case someone else does): std::endl的内联似乎影响了循环结构的早期部分(我不太明白它在做什么,但我会在这里发布以防其他人这样做):

With original code and -O2 :使用原始代码和-O2

L2:
movl    %esi, 28(%esp)
movl    28(%esp), %eax
movl    $LC0, (%esp)
movl    %eax, 4(%esp)
call    _printf
movl    __ZSt4cout, %eax
movl    -12(%eax), %eax
movl    __ZSt4cout+124(%eax), %ebx
testl   %ebx, %ebx
je  L10
cmpb    $0, 28(%ebx)
je  L3
movzbl  39(%ebx), %eax
L4:
movsbl  %al, %eax
addl    $715827883, %esi
movl    %eax, 4(%esp)
movl    $__ZSt4cout, (%esp)
call    __ZNSo3putEc
movl    %eax, (%esp)
call    __ZNSo5flushEv
jmp L2                  // no test

With mymanual inlining of std::endl , -O2 :使用std::endl-O2 mymanual 内联:

L3:
movl    %ebx, 28(%esp)
movl    28(%esp), %eax
addl    $715827883, %ebx
movl    $LC0, (%esp)
movl    %eax, 4(%esp)
call    _printf
movl    $10, 4(%esp)
movl    $__ZSt4cout, (%esp)
call    __ZNSo3putEc
movl    $__ZSt4cout, (%esp)
call    __ZNSo5flushEv
cmpl    $-1431655764, %ebx
jne L3
xorl    %eax, %eax

One difference between these two is that %esi is used in the original , and %ebx in the second version;这两者之间的一个区别是%esi在原始版本中使用,而%ebx在第二个版本中使用; is there any difference in semantics defined between %esi and %ebx in general?一般来说, %esi%ebx之间定义的语义有什么区别吗? (I don't know much about x86 assembly). (我不太了解 x86 汇编)。

Another example of this error being reported in gcc is when you have a loop that executes for a constant number of iterations, but you are using the counter variable as an index into an array that has less than that number of items, such as:在 gcc 中报告此错误的另一个示例是,当您有一个循环执行恒定次数的迭代时,但您使用 counter 变量作为索引,该数组的项目数少于该数量,例如:

int a[50], x;

for( i=0; i < 1000; i++) x = a[i];

The compiler can determine that this loop will try to access memory outside of the array 'a'.编译器可以确定此循环将尝试访问数组“a”之外的内存。 The compiler complains about this with this rather cryptic message:编译器用这个相当神秘的消息抱怨这个:

iteration xxu invokes undefined behavior [-Werror=aggressive-loop-optimizations]迭代 xxu 调用未定义的行为 [-Werror=aggressive-loop-optimizations]

What I cannot get is why i value is broken by that overflow operation?我无法得到的是为什么我的值被溢出操作破坏了?

It seems that integer overflow occurs in 4th iteration (for i = 3 ).似乎整数溢出发生在第 4 次迭代(对于i = 3 )。 signed integer overflow invokes undefined behavior . signed整数溢出调用未定义的行为 In this case nothing can be predicted.在这种情况下,什么都无法预测。 The loop may iterate only 4 times or it may go to infinite or anything else!循环可能只迭代4次,也可能无限循环或其他任何东西!
Result may vary compiler to compiler or even for different versions of same compiler.结果可能因编译器而异,甚至对于同一编译器的不同版本。

C11: 1.3.24 undefined behavior: C11:1.3.24 未定义行为:

behavior for which this International Standard imposes no requirements本国际标准没有要求的行为
[ Note: Undefined behavior may be expected when this International Standard omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data. [注意:当本国际标准省略任何明确的行为定义或程序使用错误的构造或错误的数据时,可能会出现未定义的行为。 Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message) .允许的未定义行为的范围从完全忽略情况并产生不可预测的结果,在翻译或程序执行期间以环境特征的文件化方式(有或没有发布诊断消息),到终止翻译或执行(通过发布诊断消息) Many erroneous program constructs do not engender undefined behavior;许多错误的程序结构不会产生未定义的行为; they are required to be diagnosed.他们需要被诊断。 —end note ] ——尾注]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 编译器警告“警告:迭代 10u 调用未定义的行为 [-Waggressive-loop-optimizations]”,用于 M[i] ^ k; - compiler warning “warning: iteration 10u invokes undefined behavior [-Waggressive-loop-optimizations]” for M[i] ^ k; 对于看似无关的变量,g ++“warning:iteration ...调用未定义的行为” - g++ “warning: iteration … invokes undefined behavior” for Seemingly Unrelated Variable 为什么会产生正确的输出? - Why does this produce correct output? 此代码是否会产生未定义的行为,或者它只是未指定的行为? - Does this code produce Undefined Behavior or it is merely Unspecified Behavior? For 循环 - 输出比需要的多 - For loop - more in output than needed 可变模板数组调用未定义的行为 - Variadic template array invokes undefined behavior 当vector <vector <vector <int> >>大于RAM时,为什么我的程序会产生正确的输出? - Why does my program produce correct output when my vector< vector< vector<int> > > is larger than the RAM? 在数组中使用多于分配的元素具有未定义的行为 - Using more elements in array than allocated has undefined behavior 为什么每次“ for”迭代输出4次 - Why does this output 4 times for each ‘for’ iteration 正在转移std :: bitset <N> 超过N个位置未定义的行为? - Is shifting std::bitset<N> more than N positions undefined behavior?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM