未定義行為---轉自https://blog.csdn.net/qq_29169813/article/details/51416281
什麼是未定義行為
簡單地說,未定義行為是指C語言標準未做規定的行為。編譯器可能不會報錯,但是這些行為編譯器會自行處理,所以不同的編譯器會出現不同的結果,什麼都有可能發生,這是一個極大的隱患,所以我們應該儘量避免這種情況的發生。
特徵
包含多個不確定的副作用的程式碼的行為總是被認為未定義。(簡單而言, “多
個不確定副作用” 是指在同一個表示式中使用導致同一物件修改兩次或修改以後
又被引用的自增, 自減和賦值操作符的任何組合。這是一個粗略的定義)
例子
在網上了解了一番,發現未定義行為有很多,而我初出茅廬,遇到的情況不多,只有借鑑前人的經驗。總結了一些前人遇到的問題。下面三種未定義行為是前人總結的,我只是加上了一點自己的理解。有錯誤望指出。
附原文地址:
第一例(同一個表示式中有多種運算子)
在同一個表示式中多種運算子一起計算的時候,即使我們知道各符號都有自己的優先順序或者是人為的加上括號限制計算順序,但是我們卻不知道編譯器會先計算哪一段,計算順序完全取決於編譯器,所以結果並不一定按照我們預想中的輸出。
程式碼段一
int i=7;
printf(“%d”, i++*i++);
- 1
- 2
編譯器可以選擇使用變數的舊值相乘以後再對二者進行自增運算。沒有任何保證確保自增或自減會在輸出變數原值之後和對錶達式的其它部分進行計算之前立即進行。
程式碼段二
int a=5,b;
b=++a*–a;
- 1
- 2
b的值不能確定
程式碼段三
int i = 5;
int j = (++i) + (++i) + (++i);
- 1
- 2
j的值不能確定
程式碼段四
#include <stdio.h>
int main(){
int i = 0;
int a[] = {10,20,30};
int r = 1 * a[i++] + 2 * a[i++] + 3 * a[i++];
printf("%d\n", r);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
這段程式碼也並不是我們想象中的那樣按照優先順序來計算,編譯器選擇了他自己的一種套路,
此段程式碼詳細實現情況請戳連結:http://blog.jobbole.com/53211/
第二例(同一語句中各引數的求值順序)
在同一語句中,有多個表示式,我們不能確定編譯器先呼叫哪一個表示式進行運算,運算之後又會對另一個表示式產生影響,因為他不一定是按照我們想象中自左向右進行呼叫的。
程式碼段一
printf("%d,%d\n",++n,power(2,n));
- 1
程式碼段二
int f(int a, int b);
int i = 5;
f(++i, ++i);
- 1
- 2
- 3
第三例(通過指標修改const常量的值)
編譯器對於向常量所在記憶體賦值這件事的處理是未定義的。即在對常量的記憶體操作也許並不是我們想象的那樣。
程式碼段一
int main()
{
const int a = 1;
int *b = (int*)&a;
*b = 21;
printf("%d, %d", a, *b);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
總結
個人認為,未定義行為實在難以確切的分類出來,幽默一點的說就是,水實在很深,所以我只是簡單瞭解一下,更多的情況也只有日後再程式設計過程中慢慢積累。前輩的文章也著實很詳細,看了之後基本上就會有一個大致的瞭解了。
練習題
解析:根據上面的總結,A選項,我的理解是我們不知道編譯器會怎麼選擇自增和賦值的順序,所以這是由編譯器決定的,屬於未定義行為。B選項,”hello“這個字串屬於一個字串常量了,指標p指向了這個字串常量,下一語句通過這個指標來直接修改常量第二個字元,這也屬於未定義行為。選項C,只是通過指標找到第二個字元並將它賦值給一個字元變數,並沒有改變這個字串常量,所以不屬於未定義行為。選項D,在printf語句中,i++和i–誰先執行由編譯器決定,這是未定義行為。故此題選C。