[英]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 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)循环条件总是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使用调试标志进行调试
-ftrapv
traps the program on overflow,对于整数溢出-ftrapv
在溢出时捕获程序,-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 Regehr在GCC 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
推断,因为s
在s->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.结果可能因编译器而异,甚至对于同一编译器的不同版本。
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.