C標準為編譯器提供了很大的優勢來執行優化。 如果您假設一個簡單的程序模型,其中未初始化的內存被設置為某個隨機位模式,并且所有操作都按照它們的寫入順序執行,那么這些優化的后果可能會令人驚訝。
注意:以下示例僅有效,因為x從未使用其地址,因此它是“類似寄存器”。 如果x = - x的類型具有陷阱表示,它們也將有效; 這對于無符號類型來說很少見(它需要“浪費”至少一位存儲空間,并且必須記錄在案),而-x則不可能。如果x有簽名類型,那么實現可以定義不是 - (2n-1-1)和2n-1-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 = - x尚未初始化,因此(編譯器的原因)第3行必須是由于編譯器不夠聰明的其他條件而不能發生的某種僥幸。 由于在第4行之后未使用-x,并且在第5行之前未使用x,因此可以對兩個變量使用相同的寄存器。 所以這個小程序編譯成寄存器上的以下操作:
r1 = 0;
r0 = 4;
r0 = - r0;
r1 += r0;
r0 = r1;
最終值x = - x是最終值-x,最終值x是最終值x.這些值是x = -3和y = -4,而不是5和4,如果x = some_value()已經發生 正確初始化。
有關更詳細的示例,請考慮以下代碼片段:
unsigned i, x;
for (i = 0; i < 10; i++) {
x = (condition() ? some_value() : -x);
}
假設編譯器檢測到x = - x沒有副作用。 由于-x不修改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在評估-x時未初始化。
x = - x具有未定義的行為,因此它的值是任何方便的。
優化規則? 價值:價值適用,所以這段代碼可以簡化為; 值。
當遇到問題中的代碼時,同一個編譯器會分析當評估x = - x時,-x的值是方便的。 因此,可以優化分配。
我沒有找到一個行為如上所述的編譯器的例子,但它是優秀的編譯器試圖做的優化。 遇到一個我不會感到驚訝。 這是程序崩潰的編譯器的一個不太合理的例子。 (如果在某種高級調試模式下編譯程序,可能不會令人難以置信。)
這個假設的編譯器將每個變量映射到不同的內存頁面并設置頁面屬性,以便從未初始化的變量讀取會導致調用調試器的處理器陷阱。 首先對變量賦值,確保其內存頁面正常映射。 此編譯器不會嘗試執行任何高級優化 - 它處于調試模式,旨在輕松定位諸如未初始化變量之類的錯誤。 當評估x = - x時,右側會導致陷阱并且調試器將啟動。