C 標準は、コンパイラに最適化を実行するための多くの自由を与えます。初期化されていないメモリがランダムなビット パターンに設定され、すべての操作が記述された順序で実行される単純なプログラム モデルを想定すると、これらの最適化の結果は驚くべきものになる可能性があります。
注: 次の例はx
、アドレスが取得されないため有効なだけであり、「レジスタのような」ものです。x
タイプがトラップ表現を持っている場合にも有効です。これは unsigned 型の場合はめったになく (少なくとも 1 ビットのストレージを「浪費」する必要があり、文書化する必要があります) unsigned char
、. 符号付きの型がある場合、実装は -(2 n-1 -1) と 2 n-1x
-1の間の数値ではないビット パターンをトラップ表現として定義できます。Jens Gustedt の回答を参照してください。
レジスタはメモリよりも高速であるため、コンパイラはレジスタを変数に割り当てようとします。プログラムは、プロセッサが持っているレジスタよりも多くの変数を使用する可能性があるため、コンパイラはレジスタ割り当てを実行します。これにより、異なる変数が異なるタイミングで同じレジスタを使用することになります。プログラムフラグメントを検討してください
unsigned x, y, z; /* 0 */
y = 0; /* 1 */
z = 4; /* 2 */
x = - x; /* 3 */
y = y + z; /* 4 */
x = y + 1; /* 5 */
行 3 が評価されるとき、x
まだ初期化されていないため、(コンパイラの理由により) 行 3 は、コンパイラが理解できるほど賢くなかったという他の条件のために発生することのないある種のまぐれであるに違いありません。z
は 4 行目以降とx
5 行目以前では使用されていないため、両方の変数に同じレジスタを使用できます。したがって、この小さなプログラムは、レジスタに対する次の操作にコンパイルされます。
r1 = 0;
r0 = 4;
r0 = - r0;
r1 += r0;
r0 = r1;
の最終値x
は の最終値でありr0
、 の最終値y
は の最終値ですr1
。x
これらの値は x = -3 および y = -4 であり、適切に初期化された場合に発生する 5 および 4 ではありません。
より複雑な例として、次のコード フラグメントを検討してください。
unsigned i, x;
for (i = 0; i < 10; i++) {
x = (condition() ? some_value() : -x);
}
condition
副作用がないことをコンパイラが検出したとします。condition
は を変更しないため、コンパイラは、ループの最初の実行がまだ初期化されていないため、x
アクセスできない可能性があることを認識しています。x
したがって、ループ本体の最初の実行は と同等x = some_value()
であり、条件をテストする必要はありません。コンパイラは、あなたが書いたかのようにこのコードをコンパイルするかもしれません
unsigned i, x;
i = 0; /* if some_value() uses i */
x = some_value();
for (i = 1; i < 10; i++) {
x = (condition() ? some_value() : -x);
}
これをコンパイラ内でモデル化する方法は、初期化されていない限り、に依存するすべての値が便利な値x
を持つと見なすことです。変数が単に指定されていない値を持つのではなく、初期化されていない変数の動作が未定義であるため、コンパイラは、便利な値の間の特別な数学的関係を追跡する必要はありません。したがって、コンパイラは上記のコードを次のように分析します。x
- 最初のループ反復中に、が評価
x
されるまでに初期化されません-x
。
-x
には未定義の動作があるため、その値は都合のよいものです。
- 最適化ルールが適用されるため、このコードは に簡略化できます。
condition ? value : value
condition; value
あなたの質問のコードに直面したとき、この同じコンパイラは、x = - x
が評価されたときに、 の値が-x
便利なものであることを分析します。したがって、割り当てを最適化して取り除くことができます。
上記のように動作するコンパイラの例を探したことはありませんが、これは優れたコンパイラが行おうとしている種類の最適化です。遭遇しても驚かない。これは、プログラムがクラッシュするコンパイラのあまり妥当でない例です。(ある種の高度なデバッグ モードでプログラムをコンパイルする場合、それほど信じがたいことではないかもしれません。)
この架空のコンパイラは、すべての変数を別のメモリ ページにマップし、ページ属性を設定して、初期化されていない変数からの読み取りがデバッガを呼び出すプロセッサ トラップを引き起こすようにします。変数への代入は、最初にそのメモリ ページが正常にマップされていることを確認します。このコンパイラは、高度な最適化を実行しようとはしません。初期化されていない変数などのバグを簡単に見つけることを目的としたデバッグ モードです。がx = - x
評価されると、右側でトラップが発生し、デバッガーが起動します。