C++未定義行為

wenli7363發表於2024-04-13

0 前言

未定義行為(Undefined Behavior)是指語言標準未做規定的行為。同時,標準也從沒要求編譯器判斷未定義行為,所以這些行為有編譯器自行處理,在不同的編譯器可能會產生不同的結果,又或者如果程式呼叫未定義的行為,可能會成功編譯,甚至一開始執行時沒有錯誤,只會在另一個系統上,甚至是在另一個日期執行失敗。當一個未定義行為的例項發生時,正如語言標準所說,“什麼事情都可能發生”,也許什麼都沒有發生。

1 常見未定義

1.1 未定義的結果

  1. 當我們賦給帶符號型別一個超出它表示範圍的值時,結果是未定義的。
signed char c2 = 256; // c2的值是未定義的
  1. 函式體之內定義的變數:未初始化(uninitialized),其值undefined。

  2. 算術表示式有可能產生未定義的結果

  3. 數學性質本身:除數為0

  4. 計算機的特點:溢位;很多系統在編譯和執行時都不報出溢位錯誤,像其他未定義的行為一樣,溢位的結果是不可預知的。

1.2 未定義的行為

未定義行為,無法預估Runtime會發生什麼(unpredictable:normal、crashing、incorrect results)。

  1. 解引用空指標、非法迭代器或者尾後迭代器都是未定義行為

  2. 訪問一個無效陣列索引,下標越界

  3. 當derived class物件經由一個base class指標被刪除,而該base class帶著一個non-virtual解構函式,其結果是未定義的。實際執行時通常發生的是物件的derived成員沒有被銷燬。

  4. 在兩個異常同時存在的情況下,程式若不是結束執行就是導致未定義行為。

  5. 釋放一個非new分配的記憶體,或者將相同的指標值釋放多次,其行為是未定義的。

  6. string s(s2,pos2); // s是string s2從下標pos2開始的字元複製,如果pos2>s2.size(),建構函式的行為未定義

  7. 試圖比較兩個無關地址是未定義行為

  8. 對於那些沒有指定執行順序的運算子來說,如果表示式指向並修改了同一個物件,將會引發錯誤併產生未定義的行為。

int i=0;
cout<<i<<" "<<++i<<endl; // 未定義
// 編譯器可能先求++i的值,再求i的值;也可能先求i的值,再求++i的值。注意與print函式的區別。
*beg=toupper(*beg++); // 未定義
  1. 對有符號數進行左移操作可能會改變符號位的值,因此是一種未定義的行為。移位運算子右側的運算物件一定不能為負,而且值必須嚴格小於結果的位數,否則就會產生未定義的行為。

  2. 使用static_cast將void*轉換成其他型別指標,必須確保轉換後所得的型別就是指標所指的型別。型別一旦不符,將產生未定義行為。

double d;
void* p=&d;
double *dp=static_cast<double*>(p);
  1. const_cast只能改變運算物件的底層const,如果物件本身是一個常量,使用const_cast執行寫操作就會產生未定義行為。

  2. 不要使用get初始化另一個智慧指標或為智慧指標賦值,否則將會產生兩個獨立的shared_ptr指向相同的記憶體,這將產生未定義行為。

  3. delete []p;如果忘記[],其行為是未定義的。 刪除單一物件的指標加[],其行為也是未定義的。

相關文章