forループ内の重要なコードを省略したと仮定します
forここにいるほとんどの人は、クラッシュを再現できないようです (私も含めて)。ここでの他の回答は、ループの本体で重要なコードをいくつか省略し、不足しているコードが原因であるという仮定に基づいているようです。クラッシュ。
を使用iしてループの本体でメモリ (おそらく文字列内の文字) にアクセスしていてfor、最小限の例を提供するためにそのコードを質問から除外した場合、クラッシュは次の事実によって簡単に説明できs.length() - 3ます。SIZE_MAX符号なし整数型の剰余算術による 値。SIZE_MAXは非常に大きな数であるためi、segfault をトリガーするアドレスへのアクセスに使用されるまで、大きくなり続けます。
forただし、ループの本体が空の場合でも、コードは理論的にそのままクラッシュする可能性があります。クラッシュする実装については知りませんが、コンパイラと CPU が特殊なものである可能性があります。
次の説明は、質問でコードを省略したことを前提としていません。質問に投稿したコードがそのままクラッシュするという信念が必要です。クラッシュする他のコードの省略された代役ではないこと。
最初のプログラムがクラッシュする理由
最初のプログラムがクラッシュするのは、それがコード内の未定義の動作に対する反応だからです。(コードを実行しようとすると、未定義の動作に対する実装の反応であるため、クラッシュせずに終了します。)
未定義の動作は、オーバーフローによるものintです。C++11 標準は次のように述べています ([expr] 節 5 段落 4 内):
式の評価中に、結果が数学的に定義されていないか、その型の表現可能な値の範囲内にない場合、動作は未定義です。
サンプル プログラムでは、値 2s.length()の a が返されます。それから 3 を引くと、符号なし整数型size_tを除いて、負の 1 になります。size_tC++11 標準は次のように述べています ([basic.fundamental] 節 3.9.1 パラグラフ 4):
宣言された符号なし整数は、 2 nunsignedを法とする算術法則に従うものとします。ここで、nは、整数の特定のサイズの値表現のビット数です。46
46) これは、結果の符号なし整数型で表現できない結果が、結果の符号なし整数型で表現できる最大値よりも 1 大きい数値を法として減らされるため、符号なし算術演算がオーバーフローしないことを意味します。
これは、 の結果がs.length() - 3with size_tvalueであることを意味しますSIZE_MAX。これは非常に大きな数値であり、INT_MAX( で表現できる最大値int) よりも大きくなります。
s.length() - 3は非常に大きいため、 に到達するまでループ内で実行がスピンiしますINT_MAX。次の反復で をインクリメントしようとするとi、結果はINT_MAX+ 1 になりますが、それは の表現可能な値の範囲内ではありませんint。したがって、動作は未定義です。あなたの場合、動作はクラッシュすることです。
私のシステムでは、iが過去にインクリメントされたときの実装の動作INT_MAXは、ラップ ( に設定)iしINT_MINて続行することです。-1にi達すると、通常の算術変換 (C++ [expr] 節 5 段落 9) によりi等号が発生するSIZE_MAXため、ループは終了します。
どちらの反応も適切です。これが未定義の動作の問題です。意図したとおりに動作したり、クラッシュしたり、ハード ドライブをフォーマットしたり、Firefly をキャンセルしたりする可能性があります。あなたは、決して知らない。
2 番目のプログラムがクラッシュを回避する方法
最初のプログラムと同様に、はvalue をs.length() - 3持つ型です。ただし、今回は値が に割り当てられています。C++11 標準は次のように述べています ([conv.integral] 節 4.7 パラグラフ 3):size_tSIZE_MAXint
宛先の型が符号付きの場合、宛先の型 (およびビット フィールド幅) で表現できる場合、値は変更されません。それ以外の場合、値は実装定義です。
値SIZE_MAXが で表現するには大きすぎるintためlen、実装定義の値を取得します (おそらく -1 ですが、そうでない場合もあります)。に割り当てられた値に関係なく、条件i < lenは最終的に真になるlenため、プログラムは未定義の動作に遭遇することなく終了します。