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