[解決済み] なぜこのループは「warning: iteration 3u invokes undefined behavior」を生成し、4行以上出力するのでしょうか?
質問
これをコンパイルする。
#include <iostream>
int main()
{
for (int i = 0; i < 4; ++i)
std::cout << i*1000000000 << std::endl;
}
と
gcc
は以下のような警告を出します。
warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
std::cout << i*1000000000 << std::endl;
^
符号付き整数のオーバーフローがあるとのことですが。
私が理解できないのは、なぜ
i
の値は、そのオーバーフロー操作によって壊れているのですか?
の回答を読みました。 GCCを使用したx86で整数のオーバーフローが無限ループを引き起こすのはなぜですか? については、まだよく分かっていません。 なぜ 未定義ということは、何が起きてもおかしくないということなのでしょうが、その根本的な原因は何なのでしょうか? この特定の動作 ?
オンライン http://ideone.com/dMrRKR
コンパイラです。
gcc (4.8)
解決方法は?
符号付き整数のオーバーフロー(厳密には、符号なし整数のオーバーフローは存在しない)とは 未定義の動作 . そして、これは何でも起こりうるということであり、C++のルールの下でなぜそれが起こるのかを議論しても意味がないのです。
C++11のドラフトN3337。§5.4: 1
式の評価中に、結果が数学的に定義されていない、または範囲内にない場合 その型に対して表現可能な値であれば、その動作はundefinedとなる。[注意:既存のC++の実装のほとんどは は整数のオーバーフローを無視する。ゼロによる除算、ゼロ除数を用いた余りの形成、およびすべての 浮動小数点例外はマシンによって異なり、通常はライブラリ関数で調整します。-注を終了する ]。
でコンパイルしたあなたのコードは
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)
^
このプログラムが何を行っているかを分析する唯一の方法は、生成されたアセンブリコードを読むことです。
以下は、完全なアセンブリリストです。
.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
アセンブリを読むのもやっとの私でも
addl $1000000000, %edi
という行があります。
出来上がったコードは次のようになります。
for(int i = 0; /* nothing, that is - infinite loop */; i += 1000000000)
std::cout << i << std::endl;
この@T.C.さんのコメント
というようなものではないかと思います。(1) なぜなら
i
が2より大きい場合、未定義の動作になる -> (2)i <= 2
最適化のために -> (3) ループ条件は常に真である -> (4) 最適化されて無限ループになる。
は、OPのコードのアセンブリコードと、未定義の動作がない次のコードのアセンブリコードを比較するアイデアを与えてくれました。
#include <iostream>
int main()
{
// changed the termination condition
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 // here it is
jne L7
lea esp, [ebp-16]
xor eax, eax
pop ecx
; ...snip...
残念ながら、これがバグだらけのコードを書いた結果なのです。
幸いなことに、よりよい診断やデバッグのためのツールを利用することができます。
-
すべての警告を有効にする
-
-Wall
は、すべての有用な警告を誤検出なく有効にするgccオプションです。これは、常に使用すべき最低限のものです。 -
gccには他にも多くの警告オプションがあります。 で有効化されません。
-Wall
誤認識で警告する可能性があるからです。 -
Visual C++は残念ながら、有用な警告を出す機能が遅れています。少なくとも、IDEはデフォルトでいくつか有効にしています。
-
デバッグ用フラグを使用する
-
整数のオーバーフローに対応
-ftrapv
はオーバーフロー時にプログラムをトラップします。 -
Clangコンパイラはこの点で優れています。
-fcatch-undefined-behavior
は、未定義の動作の多くのインスタンスをキャッチします(注。"a lot of" != "all of them"
)
-
整数のオーバーフローに対応
私が書いたのではない、スパゲッティのようなプログラムを明日出荷する必要があるのです。HELP!!!111oneone
gccの
-fwrapv
このオプションは、符号付き算術の加算、減算、乗算のオーバーフローは、2 進数の補数表現で折り返すと仮定するようにコンパイラに指示します。
1 - このルールは、§3.9.1.4 に次のように書かれているように、"符号なし整数のオーバーフロー" には適用されません。
<ブロッククオート符号なしと宣言された符号なし整数は,算術のモジュロ2の法則に従わなければならない。 n ここで、nは そのサイズの整数の値表現におけるビットの数。
の結果、例えば
UINT_MAX + 1
は数学的に定義されています - 算術のモジュロ2の規則によって
n
関連
-
[解決済み】C++ クラスヘッダが含まれているときに「不明な型」があるのはなぜですか?重複
-
[解決済み] error: 'ostream' does not name a type.
-
[解決済み】致命的なエラー LNK1169: ゲームプログラミングで1つ以上の多重定義されたシンボルが発見された
-
[解決済み] 既に.objで定義されている-二重包含はない
-
[解決済み】浮動小数点例外エラーが発生する: 8
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み】CMakeエラー at CMakeLists.txt:30 (project)。CMAKE_C_COMPILER が見つかりませんでした。
-
[解決済み] 数値定数の前にunqualified-idを付けて、数値を定義することを期待する。
-
[解決済み】なぜこれらのコンストラクトはプリインクリメントとポストインクリメントを使用して未定義の動作をしているのでしょうか?
-
[解決済み] なぜGCCでx86の整数オーバーフローは無限ループを引き起こすのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】コンストラクターでのエラー:識別子を期待されますか?
-
[解決済み】C-stringを使用すると警告が表示される。"ローカル変数に関連するスタックメモリのアドレスが返される"
-
[解決済み】C++でユーザー入力を待つ【重複あり
-
[解決済み】C++ 式はポインタからオブジェクトへの型を持っている必要があります。
-
[解決済み】C++の変数はイニシャライザーを持っているが、不完全な型?
-
[解決済み】エラー。switchステートメントでcaseラベルにジャンプする
-
[解決済み】なぜ、サイズ8の初期化されていない値を使用するのでしょうか?
-
[解決済み] 警告:暗黙の定数変換でのオーバーフロー
-
[解決済み】なぜこのforループはあるプラットフォームでは終了し、他のプラットフォームでは終了しないのでしょうか?
-
[解決済み] なぜGCCでx86の整数オーバーフローは無限ループを引き起こすのですか?