C++ 为什么这个循环会产生“警告:迭代 3u 调用未定义的行为”并输出超过 4 行?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24296571/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does this loop produce "warning: iteration 3u invokes undefined behavior" and output more than 4 lines?
提问by zerkms
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?, but I'm still not clear on whythis happens - I get that "undefined" means "anything can happen", but what's the underlying cause of this specific behavior?
我已经阅读了为什么带有 GCC 的 x86 上的整数溢出会导致无限循环的答案?,但我仍然不清楚为什么会发生这种情况 - 我认为“未定义”意味着“任何事情都可能发生”,但是这种特定行为的根本原因是什么?
Online: http://ideone.com/dMrRKR
Compiler: gcc (4.8)
编译器: gcc (4.8)
采纳答案by milleniumbug
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 de?ned or not in the range of representable values for its type, the behavior is unde?ned. [ Note: most existing implementations of C++ ignore integer over?ows. Treatment of division by zero, forming a remainder using a zero divisor, and all ?oating point exceptions vary among machines, and is usually adjustable by a library function. —end note ]
如果在对表达式求值期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义。[ 注意:大多数现有的 C++ 实现都忽略整数溢出。除以零的处理、使用零除数形成余数以及所有浮点异常的处理因机器而异,通常可以通过库函数进行调整。——尾注]
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
.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 , %esp
call ___main
L4:
movl %edi, (%esp)
movl $__ZSt4cout, %ecx
call __ZNSolsEi
movl %eax, %esi
movl (%eax), %eax
subl , %esp
movl -12(%eax), %eax
movl 124(%esi,%eax), %ebx
testl %ebx, %ebx
je L15
cmpb for(int i = 0; /* nothing, that is - infinite loop */; i += 1000000000)
std::cout << i << std::endl;
, 28(%ebx)
je L5
movsbl 39(%ebx), %eax
L6:
movl %esi, %ecx
movl %eax, (%esp)
addl 00000000, %edi
call __ZNSo3putEc
subl , %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 , %eax
cmpl $__ZNKSt5ctypeIcE8do_widenEc, %edx
je L6
movl , (%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 , %esp
.cfi_def_cfa_offset 32
movl $__ZStL8__ioinit, %ecx
call __ZNSt8ios_base4InitC1Ev
movl $___tcf_0, (%esp)
call _atexit
addl , %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
我什至几乎看不懂汇编,但即使我能看到这addl $1000000000, %edi
条线。结果代码看起来更像
#include <iostream>
int main()
{
// changed the termination condition
for (int i = 0; i < 3; ++i)
std::cout << i*1000000000 << std::endl;
}
This comment of @T.C.:
@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 thati <= 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) 循环条件始终为真 -> (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 代码的汇编代码与以下代码的汇编代码进行比较的想法,没有未定义的行为。
; ...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...
And, in fact, the correct code has termination condition.
而且,事实上,正确的代码具有终止条件。
warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
std::cout << i*1000000000 << std::endl;
^
OMG, that's completely not obvious! It's not fair! I demand trial by fire!
OMG,这完全不明显!这不公平!我要火试!
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"
)
- for integer overflow
启用所有警告
-Wall
是 gcc 选项,它启用所有有用的警告而没有误报。这是您应该始终使用的最低限度。- gcc 有许多其他警告选项,但是,它们没有启用,
-Wall
因为它们可能会在误报时发出警告 - 不幸的是,Visual C++ 在提供有用警告的能力方面落后了。至少 IDE 默认启用了一些。
使用调试标志进行调试
- 对于整数溢出会
-ftrapv
在溢出时捕获程序, - 锵编译器非常适合这样的:
-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 2nwhere n is the number of bits in the value representation of that particular size of integer.
声明为无符号的无符号整数应遵守算术模 2 n的法则,其中 n 是该特定整数大小的值表示中的位数。
and e.g. result of UINT_MAX + 1
is mathematically defined - by the rules of arithmetic modulo 2n
并且例如结果UINT_MAX + 1
是数学定义的 - 由算术模 2 n的规则
回答by Shafik Yaghmour
Short answer, gcc
specifically has documented this problem, we can see that in the gcc 4.8 release noteswhich 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. This may cause non-conforming programs to no longer work as expected, such as SPEC CPU 2006 464.h264ref and 416.gamess. A new option, -fno-aggressive-loop-optimizations, was added to disable this aggressive analysis. 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. The warning can be disabled with -Wno-aggressive-loop-optimizations.
GCC 现在使用更积极的分析来使用语言标准强加的约束来推导出循环迭代次数的上限。这可能会导致不符合要求的程序不再按预期工作,例如 SPEC CPU 2006 464.h264ref 和 416.gamess。添加了一个新选项 -fno-aggressive-loop-optimizations 以禁用此积极分析。在某些已知迭代次数恒定的循环中,但已知在到达之前或最后一次迭代期间在循环中发生了未定义的行为,GCC 将警告循环中的未定义行为,而不是推导出迭代次数的下上限为循环。可以使用 -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 integeroverflow is undefined behavior by looking at the draft C++ standard section 5
Expressionsparagraph 4which says:
通过查看 C++ 标准部分表达式草案第4段,知道有符号整数溢出是未定义的行为,答案很长,它说:5
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
如果在对表达式求值期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为为 undefined。[ 注意:大多数现有的 C++ 实现都忽略整数溢出。除以零的处理,使用零除数形成余数,以及所有浮点异常在机器之间有所不同,通常可以通过库函数进行调整。——尾注
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? It sounds completely wacky. But thankfully gcc
gives us a clue to figuring it out in the warning:
但是gcc
优化器到底能做些什么来把它变成一个无限循环呢?这听起来很古怪。但值得庆幸的gcc
是,在警告中为我们提供了解决问题的线索:
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 clue is the Waggressive-loop-optimizations
, what does that mean? Fortunately for us this is not the first time this optimization has broken code in this way and we are lucky because John Regehrhas documented a case in the article GCC pre-4.8 Breaks Broken SPEC 2006 Benchmarkswhich shows the following code:
线索是Waggressive-loop-optimizations
,这是什么意思?对我们来说幸运的是,这不是这种优化第一次以这种方式破坏代码,我们很幸运,因为John Regehr在GCC pre-4.8 Breaks Broken SPEC 2006 Benchmarks文章中记录了一个案例,其中显示了以下代码:
struct foo *s = ...;
int x = s->f;
if (!s) return ERROR;
the article says:
文章说:
The undefined behavior is accessing d[16] just before exiting the loop. 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.
未定义的行为是在退出循环之前访问 d[16]。在 C99 中,创建一个指向数组末尾后一个位置的元素的指针是合法的,但该指针不能被取消引用。
and later on says:
后来说:
In detail, here is what's going on. A C 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. For the code here, GCC can infer that k is in the range 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.”The situation here, where the compiler uses the assumption of well-definedness to infer a useful dataflow fact,
详细来说,这是发生了什么。AC 编译器在看到 d[++k] 时,可以假设 k 的递增值在数组边界内,否则会发生未定义的行为。对于这里的代码,GCC 可以推断出 k 的范围是 0..15。稍后,当 GCC 看到 k<16 时,它对自己说:“啊哈——这个表达式总是正确的,所以我们有一个无限循环。” 这里的情况,编译器使用定义良好的假设来推断有用的数据流事实,
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 removalwhere in seeing this code:
他解释说,这与臭名昭著的Linux 内核空指针检查删除非常相似,其中看到以下代码:
i = 0;
do {
cout << i << endl;
i += NUMBER;
}
while (i != NUMBER * 4)
gcc
inferred that since s
was deferenced in 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.
gcc
推断出由于s
被推入 s->f;
并且由于取消引用空指针是未定义的行为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.
这里的教训是,现代优化器非常热衷于利用未定义的行为,而且很可能只会变得更加激进。很明显,通过几个例子,我们可以看到优化器做了一些对程序员来说完全不合理的事情,但从优化器的角度回想起来是有道理的。
回答by M.M
tl;drThe code generates a test that integer+ positive integer== negative integer. 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. I haven't figured out what's special about endl
yet.
tl;dr代码生成一个测试,整数+正整数==负整数。通常优化器不会优化这个,但是在std::endl
接下来使用的特定情况下,编译器会优化这个测试。我还没有弄清楚有什么特别之处endl
。
From the assembly code at -O1 and higher levels, it is clear that gcc refactors the loop to:
从 -O1 和更高级别的汇编代码,很明显 gcc 将循环重构为:
L4:
movsbl %al, %eax
movl %eax, 4(%esp)
movl $__ZSt4cout, (%esp)
call __ZNSo3putEc
movl %eax, (%esp)
call __ZNSo5flushEv
addl 5827882, %esi
cmpl $-1431655768, %esi
jne L6
// fallthrough to "return" code
The biggest value that works correctly is 715827882
, i.e. floor(INT_MAX/3
). The assembly snippet at -O1
is:
正确工作的最大值是715827882
,即 floor( INT_MAX/3
)。汇编代码段-O1
是:
L4:
movsbl %al, %eax
addl 5827882, %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
Note, the -1431655768
is 4 * 715827882
in 2's complement.
注意,-1431655768
是4 * 715827882
2 的补码。
Hitting -O2
optimizes that to the following:
命中将其-O2
优化为以下内容:
L4:
movsbl %al, %eax
addl 5827883, %esi
movl %eax, 4(%esp)
movl $__ZSt4cout, (%esp)
call __ZNSo3putEc
movl %eax, (%esp)
call __ZNSo5flushEv
jmp L2
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. However, -O2 then makes a change:
如果我们重新编译,715827883
那么 -O1 版本除了更改的数字和测试值之外是相同的。但是,-O2 然后进行了更改:
#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);
}
}
Where there was cmpl $-1431655764, %esi
at -O1
, that line has been removed for -O2
. The optimizer must have decided that adding 715827883
to %esi
can never equal -1431655764
.
凡有cmpl $-1431655764, %esi
在-O1
,该行已经为删除-O2
。优化器一定已经决定添加715827883
到%esi
永远不会等于-1431655764
.
This is pretty puzzling. Adding that to INT_MIN+1
doesgenerate 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
! (this is only possible if wraparound does actually occur), yet it does not optimize the line out in that example.
在工作示例中,得出715827882
一个数字相加不能等于的结论似乎同样有效INT_MIN + 715827882 - 2
!(这只有在实际发生回绕时才有可能),但它并没有优化该示例中的线路。
The code I was using is:
我使用的代码是:
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 L3:
movl %ebx, 28(%esp)
movl 28(%esp), %eax
addl 5827883, %ebx
movl $LC0, (%esp)
movl %eax, 4(%esp)
call _printf
movl , 4(%esp)
movl $__ZSt4cout, (%esp)
call __ZNSo3putEc
movl $__ZSt4cout, (%esp)
call __ZNSo5flushEv
cmpl $-1431655764, %ebx
jne L3
xorl %eax, %eax
, 28(%ebx)
je L3
movzbl 39(%ebx), %eax
L4:
movsbl %al, %eax
addl 5827883, %esi
movl %eax, 4(%esp)
movl $__ZSt4cout, (%esp)
call __ZNSo3putEc
movl %eax, (%esp)
call __ZNSo5flushEv
jmp L2 // no test
If the std::endl(std::cout)
is removed then the optimization no longer occurs. In fact replacing it with std::cout.put('\n'); std::flush(std::cout);
also causes the optimization to not happen, even though std::endl
is inlined.
如果std::endl(std::cout)
删除了,则不再进行优化。事实上std::cout.put('\n'); std::flush(std::cout);
,即使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
:
int a[50], x;
for( i=0; i < 1000; i++) x = a[i];
With mymanual inlining of std::endl
, -O2
:
使用 mymanual 内联std::endl
, -O2
:
One difference between these two is that %esi
is used in the original , and %ebx
in the second version; is there any difference in semantics defined between %esi
and %ebx
in general? (I don't know much about x86 assembly).
这两者之间的一个区别%esi
是在原始%ebx
版本中使用,在第二个版本中使用;之间%esi
和%ebx
一般定义的语义有什么区别吗?(我不太了解 x86 汇编)。
回答by Ed Tyler
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 变量作为索引,该数组的项目数少于该数量,例如:
##代码##The compiler can determine that this loop will try to access memory outside of the array 'a'. The compiler complains about this with this rather cryptic message:
编译器可以确定此循环将尝试访问数组 'a' 之外的内存。编译器用这个相当神秘的消息抱怨这个:
iteration xxu invokes undefined behavior [-Werror=aggressive-loop-optimizations]
迭代 xxu 调用未定义的行为 [-Werror=aggressive-loop-optimizations]
回答by haccks
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
).
signed
integer overflow invokes undefined behavior. In this case nothing can be predicted. The loop may iterate only 4
times or it may go to infinite or anything else!
Result may vary compiler to compiler or even for different versions of same compiler.
似乎整数溢出发生在第 4 次迭代(对于i = 3
)。
signed
整数溢出调用未定义的行为。在这种情况下,什么都无法预测。循环可能只迭代4
几次,也可能无限循环或其他任何东西!
结果可能因编译器而异,甚至对于同一编译器的不同版本。
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 ]
本国际标准未对其施加要求的
行为 [注意:当本国际标准省略任何明确的行为定义或程序使用错误的构造或错误的数据时,可能会出现未定义的行为。允许的未定义行为的范围从完全忽略情况并产生不可预测的结果,在翻译或程序执行期间以环境特征的文件化方式(有或没有发布诊断消息),到终止翻译或执行(通过发布诊断消息)。许多错误的程序结构不会产生未定义的行为;他们需要被诊断。——尾注]