什麼是未定義行為
C或C++的標準對結果沒有任何要求的行為。例如有號整數溢位、陣列範圍外的讀寫、參考 NULL 指標、使用已釋放的區域、負值的左移等。編譯器可以假設正確的程式不會發生未定義行為來進行最佳化,因此程式可能碰巧照預期運作,也可能因最佳化設定而使判斷消失等,出現意料之外的結果。它與由處理系統決定並寫在文件中的「實作定義行為」有所區別。
日文名稱: 未定義動作
「未定義行為」出現在2領域的10道練習題中(C語言基礎, 編譯器等)。
這個名詞所屬的領域: C語言基礎
C或C++的標準對結果沒有任何要求的行為。例如有號整數溢位、陣列範圍外的讀寫、參考 NULL 指標、使用已釋放的區域、負值的左移等。編譯器可以假設正確的程式不會發生未定義行為來進行最佳化,因此程式可能碰巧照預期運作,也可能因最佳化設定而使判斷消失等,出現意料之外的結果。它與由處理系統決定並寫在文件中的「實作定義行為」有所區別。
日文名稱: 未定義動作
「未定義行為」出現在2領域的10道練習題中(C語言基礎, 編譯器等)。
這個名詞所屬的領域: C語言基礎
有號整數運算的溢位(如 INT_MAX + 1)、陣列範圍外的讀寫、使用未初始化自動變數的值、透過 NULL 指標或懸置指標讀寫、同一區域的重複釋放、以0做整數除法、移位量達型別寬度以上或負值的左移等。無號整數的溢位不是未定義行為,規定會回到除以2的冪次的餘數。
編譯器可以假設「不會發生未定義行為」,因此可能把 x + 1 > x 視為恆真,或刪除讀取 *p 之後的 p == NULL 判斷。所以在 -O0 下照預期運作的程式,可能只因改成 -O2 或升級編譯器就無法運作。這不是編譯器的錯誤,原因在於程式違反了標準的前提。
gcc 或 clang 加上 -fsanitize=undefined(UBSan)可在執行時偵測有號整數溢位等,加上 -fsanitize=address(ASan)可偵測範圍外的讀寫與釋放後使用。-Wall 或 -Wextra 的警告、靜態分析工具,以及遵循 MISRA C 等規範的寫法,也有助於減少未定義行為。
正確答案:C. int 變數的值為最大值2147483647時,再加1
像 int 的 2147483647 + 1 這樣,有號整數運算的結果超出可表示的範圍,屬於未定義行為。無號整數的運算規定會回到除以 2³² 的餘數,因此 4294967295 + 1 為0,不是未定義行為。負值的右移是由處理系統決定為算術移位或邏輯移位的實作定義行為,gcc 採用算術移位。另一方面,負值的左移是未定義行為。
實作定義行為是像 int 的大小、char 是否有號、負值右移的結果這類,由處理系統選定一種行為並寫在文件中的事項。未定義行為是標準不作任何要求的行為,即使是同一個處理系統,結果也可以因情況而異。具可攜性的程式會明示依賴實作定義行為的部分,並避免發生未定義行為。
有問題。即使在目前的環境中碰巧照預期運作,只要最佳化設定、編譯器版本或周圍的程式碼改變,結果就可能不同。從資安的角度來看,範圍外寫入這類未定義行為也可能被攻擊者利用。
有號整數的溢位是未定義行為,因此編譯器可以假設「正確的程式中不會發生溢位」。
在C語言中,有號整數的溢位是未定義行為。在此前提下,對上方函式進行最佳化的編譯器可能產生的程式碼,下列何者正確?未定義行為是標準不作任何要求的行為,例如有號整數溢位、陣列範圍外的參考、參考 NULL 指標、釋放後使用等。
關於C語言標準所說的未定義行為,下列說明何者正確?像 int 的 2147483647 + 1 這樣,有號整數運算的結果超出可表示的範圍,屬於未定義行為。
int 與 unsigned int 為32位元。下列何者依C語言的標準屬於未定義行為?相對地,有號整數型別運算的溢位是未定義行為。
uint8_t 是8位元的無號整數型別。執行上方讓 x 遞增10次的程式碼時的輸出,下列何者正確?在 fact(13) 中,最後的 13 × 479001600 乘法會發生有號整數溢位,這是未定義行為。
int 為32位元(最大值 2147483647)。上方的函式 fact(n) 能不發生溢位、正確回傳 n 的階乘的最…