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_t
C++11 標準は次のように述べています ([basic.fundamental] 節 3.9.1 パラグラフ 4):
宣言された符号なし整数は、 2 nunsigned
を法とする算術法則に従うものとします。ここで、nは、整数の特定のサイズの値表現のビット数です。46
46) これは、結果の符号なし整数型で表現できない結果が、結果の符号なし整数型で表現できる最大値よりも 1 大きい数値を法として減らされるため、符号なし算術演算がオーバーフローしないことを意味します。
これは、 の結果がs.length() - 3
with size_t
valueであることを意味します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_t
SIZE_MAX
int
宛先の型が符号付きの場合、宛先の型 (およびビット フィールド幅) で表現できる場合、値は変更されません。それ以外の場合、値は実装定義です。
値SIZE_MAX
が で表現するには大きすぎるint
ためlen
、実装定義の値を取得します (おそらく -1 ですが、そうでない場合もあります)。に割り当てられた値に関係なく、条件i < len
は最終的に真になるlen
ため、プログラムは未定義の動作に遭遇することなく終了します。