なぜこのループは「警告:反復3uが未定義の動作を呼び出す」ので、4行以上出力するのですか?


162

これをコンパイルする:

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


49
符号付き整数オーバーフロー=>未定義の動作=>鼻デーモン。しかし、私は認めなければなりません、その例は非常にいいです。
dyp 2014年

1
アセンブリ出力:goo.gl/TtPmZn
Bryan Chen

1
GCC 4.8でO2、およびO3フラグを使用して発生しますが、O0または発生しませんO1
Alex

3
鼻のデーモンを読んだとき、@ dyp何かおかしいと思ったら鼻を少しだけ吐き出す「イムグル笑い」をしました。そして気づいた...鼻の悪魔に呪われているに違いない!
corsiKa 2014年

4
これをブックマークしておくと、次に誰かが「技術的にはUBですが、何かを実行する必要があります」とレトルトするときにリンクできます。:)
MM

回答:


107

符号付き整数オーバーフロー(厳密に言うと、「符号なし整数オーバーフロー」などはありません)は、未定義の動作を意味します。そして、これは何かが起こる可能性があることを意味し、なぜそれがC ++のルールの下で起こるのかを議論することは意味がありません。

C ++ 11ドラフトN3337:§5.4:1

式の評価中に結果が数学的に定義されていないか、その型の表現可能な値の範囲内にない場合、動作は未定義です。[注: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;

@TCのこのコメント:

私はそれが次のようなものであると疑っています:(1)i2より大きい値のすべての反復は未定義の動作をするため->(2)i <= 2最適化の目的で仮定できる->(3)ループ条件は常にtrue->(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...

OMG、それは完全に明白ではありません!それは公平ではありません!火事裁判を求める!

それに対処して、バグのあるコードを書いて、気分が悪くなるはずです。結果を負担します。

...または、代わりに、より適切な診断ツールとより適切なデバッグツールを適切に使用します。

  • すべての警告を有効にする

    • -Wall誤検知なしですべての有用な警告を有効にするgccオプションです。これは最低限必要なもので、常に使用する必要があります。
    • gccには他にも多くの警告オプションがありますが-Wall誤検知について警告する可能性があるためい
    • Visual C ++は残念ながら、有用な警告を表示する機能に遅れをとっています。少なくともIDEでは、デフォルトで一部が有効になっています。
  • デバッグにデバッグフラグを使用する

    • 整数オーバーフローの場合 -ftrapvにプログラムをトラップします。
    • クランコンパイラは、このための優れている:-fcatch-undefined-behavior未定義の動作(注:のインスタンスの多くをキャッチ"a lot of" != "all of them"

明日出荷する必要のある、自分が作成したものではないプログラムのスパゲッティの混乱があります!ヘルプ!!!!!! 111oneone

gccを使用 -fwrapv

このオプションは、加算、減算、乗算の符号付き算術オーバーフローが2の補数表現を使用して折り返すと想定するようにコンパイラーに指示します。

1 -§3.9.1.4によると、このルールは「符号なし整数オーバーフロー」には適用されません。

符号なし整数は、符号なしと宣言され、2 nを法とする算術の法則に従います。ここで、nは、その特定のサイズの整数の値表現のビット数です。

そして、例えばの結果UINT_MAX + 1は数学的に定義されている-2 nを法とする算術の規則による


7
ここで何が起こっているのか、まだよくわかりません...なぜ、iそれ自体が影響を受けるのですか?一般に、未定義の動作にはこのような奇妙な副作用はありません。結局のところ、i*100000000右辺値である必要があります
vsoftco

26
私はそれが次のようなものであると疑っています:(1)i2より大きい値のすべての反復 は未定義の動作をするため->(2)i <= 2最適化の目的で仮定できる->(3)ループ条件は常にtrue->(4 )無限ループに最適化されます。
2014年

28
@vsoftco:進行しているのは、強度の低下、より具体的には、誘導変数の除去の場合です。コンパイラーは、i反復ごとに1e9ずつインクリメントするコードを発行し(それに応じてループ条件を変更)、乗算を排除します。これは「あたかも」ルールの下で完全に有効な最適化です。このプログラムは、動作が良ければ違いを観察できなかったためです。悲しいかな、そうではなく、最適化は「リーク」します。
JohannesD

8
@JohannesDは、これが失敗する理由を明らかにしました。ただし、ループの終了条件にはオーバーフローが含まれないため、これは不適切な最適化です。強度低下の使用は問題ありませんでした-プロセッサの乗数が(4 * 100000000)とどうなるか(100000000 + 100000000 + 100000000 + 100000000)とは異なり、「未定義です」にフォールバックします「誰が知っているか」は合理的です。しかし、4回実行され、未定義の結果を生成する、正常に動作するループを、4回以上実行されるものに置き換えます。ばかです。
オースティンのジュリー

14
@JulieinAustinばかげているかもしれませんが、完全に合法です。良い面として、コンパイラーは警告を出します。
ミレニアムバグ2014年

68

短い答え、gcc具体的にはこの問題が文書化されています。これは、gcc 4.8のリリースノートに次のように書かれていることがわかります(今後、重点を置く):

GCCはより積極的な分析を使用して、言語標準によって課された制約を使用したループの反復回数の上限を導き出します。これにより、SPEC CPU 2006 464.h264refや416.gamessなどの非準拠プログラムが期待どおりに機能しなくなる可能性があります。この積極的な分析を無効にするために、新しいオプション-fno-aggressive-loop-optimizationsが追加されました。反復回数が一定であることがわかっているが、最後の反復に到達する前または最後にループで未定義の動作が発生することがわかっている一部のループでは、GCCは反復回数の下限を導出する代わりに、ループ内の未定義の動作について警告します。ループのため。警告は-Wno-aggressive-loop-optimizationsで無効にできます。

確かに私たちが使用する場合 -fno-aggressive-loop-optimizations、無限ループの動作する停止する必要があり、テストしたすべてのケースで停止します。

長い答えは、C ++標準ドラフトセクションを見て、符号付き整数オーバーフローが未定義の動作であることを知ることから始まります。5 式の段落4言います:

式の評価中に結果が数学的に定義されていないか、その型の表現可能な値の範囲にない場合、動作はundefinedです。[注:C ++のほとんどの既存の実装は整数オーバーフローを無視します。ゼロによる除算の処理、ゼロ除数を使用した剰余の形成、およびすべての浮動小数点例外はマシン間で異なり、通常はライブラリー関数によって調整可能です。-エンドノート

規格には、未定義の動作は次のような定義に付属する注記から予測不可能であると記載されていることがわかっています。

[注:この国際標準が動作の明示的な定義を省略した場合、またはプログラムが誤った構成体または誤ったデータを使用した場合、未定義の動作が予想されます。 許容される未定義の動作は、予測できない結果完全に状況を無視することから、翻訳またはプログラムの実行中に、環境に特徴的な文書化された方法(診断メッセージの発行の有無にかかわらず)で動作すること、翻訳または実行を終了すること(発行の場合)までさまざまです。診断メッセージ)。エラーのあるプログラム構造の多くは、未定義の動作を引き起こしません。診断する必要があります。—エンドノート]

しかし、gccこれを無限ループに変えるために、オプティマイザは世界で何ができるでしょうか?それは完全に奇妙に聞こえます。ありがたいことにgccことに警告の中でそれを理解するための手がかりを私たちに与えます:

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

手がかりは、Waggressive-loop-optimizationsそれはどういう意味ですか?幸いなことに、この最適化によってこのようにコードが壊れたのは今回が初めてではなく、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;
}

記事は言う:

未定義の動作は、ループを終了する直前にd [16]にアクセスしています。C99では、配列の末尾を1つ超えた要素へのポインターを作成することは正当ですが、そのポインターを逆参照してはなりません。

後で言う:

詳しくは、こちらをご覧ください。Cコンパイラは、d [++ k]を確認すると、インクリメントされたkの値が配列の境界内にあると想定できます。それ以外の場合、未定義の動作が発生するためです。ここのコードでは、GCCはkが0..15の範囲にあると推測できます。少し後、GCCはk <16を検出すると、それ自体に次のように言います。「ああ、その式は常に真であるため、無限ループがあります。」ここでは、コンパイラーが明確な仮定を使用して、有用なデータフローの事実を推測している状況、

したがって、符号付き整数のオーバーフローは未定義の動作であるため、コンパイラーがいくつかのケースで実行しなければならないことは、i常により小さくなければならず4、したがって無限ループがあることを前提としています。

彼はこれが悪名高いLinuxカーネルのnullポインターチェックの削除に非常に似ていると説明します。

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

gccで参照されたためss->f;およびnullポインターの逆参照は未定義の動作sであるため、nullであってはならず、最適化されてif (!s)、次の行のチェックを。

ここでの教訓は、最新のオプティマイザは未定義の動作の悪用について非常に積極的であり、おそらくより積極的になるだけであるということです。明らかに、ほんの数例で、オプティマイザーはプログラマーにはまったく無理であるように見えますが、オプティマイザーの観点から振り返ると理にかなっています。


7
これはコンパイラの作成者が行っていることです(私はコンパイラとオプティマイザを1つまたは2つ作成するために使用しました)。ただの狂気です。上で引用した構成は間違っていますが、エラーチェックを最適化することはユーザーに敵意があります。
オースティンのジュリー

1
@JulieinAustin開発者が未定義の動作を回避する必要があることは実際には問題の半分にすぎないと言って、これはかなり驚くべき動作であることに同意します。コンパイラーも開発者により良いフィードバックを提供する必要があることは明らかです。この場合、警告は十分にわかりませんが生成されます。
Shafik Yaghmour 2014年

3
私はそれが良いことだと思います、私はより良く、より速いコードを望んでいます。UBは絶対に使用しないでください。
ポール、2014年

1
@paulm道徳的にUBは明らかに悪いですが、プロダクションアプリケーションに影響を与える前に開発者がUBやその他の問題を発見できるように、clang静的アナライザーなどのより優れたツールを提供することは議論が困難です。
Shafik Yaghmour 2014年

1
@ShafikYaghmourまた、開発者が警告を無視している場合、clangの出力に注意を払う可能性はありますか?この問題は、積極的な「正当化されていない警告なし」ポリシーによって簡単に捕捉できます。Clangは推奨されますが、必須ではありません。
deworde 2014年

24

tl; drこのコードは、整数 + 正の整数 == 負の整数というテストを生成します。通常、オプティマイザはこれを最適化しませんが、std::endl次に使用される特定のケースでは、コンパイラはこのテストを最適化します。何が特別なのかendlまだわかりません。


-O1以上のレベルのアセンブリコードから、gccがループを次のようにリファクタリングしていることは明らかです。

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

正しく動作する最大の値は715827882、つまり、floor(INT_MAX/3)です。のアセンブリスニペット-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

注、-1431655768ある4 * 7158278822の補数で。

打つ-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

したがって、行われた最適化は、それがaddl上位に移動されたことにすぎません。

715827883代わりに再コンパイルすると、変更された数とテスト値を除いて、-O1バージョンは同じになります。ただし、-O2は変更を加えます。

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

にあった場所cmpl $-1431655764, %esi-O1、その行は削除されました-O2。オプティマイザは、に追加715827883する%esiことは決して等しくなることができないと決定したに違いありません-1431655764

これはかなり不可解です。これを追加すると期待どおりの結果INT_MIN+1 生成されるので、オプティマイザはそう%esiなることは決してないINT_MIN+1だろうと判断したに違いありません。なぜそうなるのかはわかりません。

実際の例では715827882、数値への加算は等しくないという結論を下すことも同様に有効だと思われますINT_MIN + 715827882 - 2。(これは実際にラップアラウンドが発生した場合にのみ可能です)、それでもその例ではラインアウトを最適化していません。


私が使用していたコードは:

#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);
    }
}

std::endl(std::cout)削除すると、最適化は行われなくなります。実際、これを置き換えると、インラインstd::cout.put('\n'); std::flush(std::cout);化されていても最適化は行われませんstd::endl

のインライン化 std::endl、ループ構造の最初の部分に影響を与えているようです(何をしているのかよくわかりませんが、誰かがそうした場合に備えて、ここに投稿します)。

元のコードと-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

mymanual inlineingを使用してstd::endl-O2

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

これら2つの違いの1つは%esi、元のと%ebx2番目のバージョンで使用されていることです。間で定義された意味のいずれかの差がある%esi%ebx一般的には?(x86アセンブリについてはあまり知りません)。


オプティマイザのロジックが何であるかを正確に確認するのは良いことですが、この段階では、なぜテストが最適化されているのか、そうでないのかが分かりません
MM

8

gccで報告されるこのエラーのもう1つの例は、一定の反復回数で実行されるループがあるが、次のように、その数より少ない項目を持つ配列へのインデックスとしてカウンター変数を使用している場合です。

int a[50], x;

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

コンパイラは、このループが配列 'a'の外のメモリにアクセスしようとすることを決定できます。コンパイラは、このかなり不可解なメッセージでこれについて不平を言います:

反復xxuが未定義の動作を呼び出します[-Werror = aggressive-loop-optimizations]


さらに不可解なのは、最適化がオンになっている場合にのみメッセージが送信されることです。M $ VBメッセージ「範囲外のアレイ」はダミー向けですか?
Ravi Ganesh

6

私が得ることができないのは、なぜオーバーフロー操作によって値が壊れているのですか?

整数オーバーフローが4番目の反復(の場合i = 3)で発生しているようです。 signed整数オーバーフローは未定義の動作を引き起こします。この場合、何も予測できません。ループは4何度も繰り返されるか、無限または他の何かに進む可能性があります!
結果はコンパイラごとに異なる場合があります。また、同じコンパイラの異なるバージョンでも異なる場合があります。

C11:1.3.24未定義の動作:

この国際標準が要件を課さない
動作[注:この国際標準が動作の明示的な定義を省略した場合、またはプログラムが誤った構成体または誤ったデータを使用した場合、未定義の動作が予期される可能性があります。許容される未定義の動作は、予測できない結果で完全に状況を無視することから、環境に特徴的な文書化された方法で(診断メッセージの発行の有無にかかわらず)変換またはプログラムの実行中に動作すること、変換または実行を終了すること(発行とともに)までの範囲です。診断メッセージの)。エラーのあるプログラム構造の多くは、未定義の動作を引き起こしません。診断する必要があります。—エンドノート]


@bits_international; はい。
2014年

4
私が反対票を投じた理由を説明するのは公平です。この回答の情報は正しいですが、教育的ではなく、部屋の象は完全に無視されます。破損は、オーバーフローを引き起こした操作とは別の場所(停止条件)で発生したようです。これがこの質問の核心であるにもかかわらず、この特定のケースで物事がどのように壊れるかのメカニズムは説明されていません。これは、教師の答えが問題の核心に対処しないだけでなく、それ以上の質問を思いとどまらせる典型的な悪い教師の状況です。それはほとんどのように聞こえます...
Szabolcs

5
「これは未定義の動作であり、この時点から、それがどのようにまたはなぜ壊れるかは気にしません。標準ではそれが壊れることを許可しています。これ以上の質問はありません。」そういう意味ではなかったかもしれませんが、そのようです。私は、SOに対するこの(残念ながら一般的な)態度が少なくなることを期待しています。これは実用的ではありません。ユーザー入力を受け取っ場合、たとえそれが原因でプログラムの他の部分が爆発する可能性があると規格が言っているとしても、すべての単一の符号付き整数演算の後にオーバーフローをチェックすることは合理的ではありません。理解どのようにそれが壊れることはありません実際には、このようなヘルプ回避の問題を。
Szabolcs 2014年

2
@Szabolcs:Cを2つの言語と考えるのが最適かもしれません。そのうちの1つは、意図したターゲットプラットフォームでは信頼できるが、そうではない構造を悪用するプログラマーの助けを借りて、シンプルなコンパイラーが合理的に効率的な実行可能コードを実現できるように設計されましたその他、およびその結果、標準委員会によって無視されました。また、標準がサポートを義務付けていないそのような構成要素をすべて除外する第2言語は、コンパイラーが追加の最適化を適用できるようにする目的で、プログラマーが必要とするものを上回るか、または下回らない場合があります。あきらめる。
スーパーキャット

1
@Szabolcs " ユーザー入力を取得する場合、すべての単一の符号付き整数演算の後にオーバーフローをチェックすることは合理的ではありません "-その時点では手遅れなので修正します。すべての符号付き整数演算のにオーバーフローをチェックする必要があります。
メルポメン2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.