1. 程式人生 > 其它 >【今日問題】變數未初始化引起的崩潰

【今日問題】變數未初始化引起的崩潰

昨天寫的今日問題,有小夥伴給我反饋,覺得挺有用,小編今天繼續給小夥伴們總結遇到的常見問題

一、初學者經常由於沒有養成良好的程式設計習慣,未初始化變數會引起那些問題

使用未初始化的變數是常見的程式錯誤,通常也是難以發現的錯誤。雖然許多編譯器都至少會提醒不要使用未初始化變數,但是編譯器並未被要求去檢測未初始化變數的使用。而且,沒有一個編譯器能檢測出所有未初始化變數的使用。

現象列舉:

1、引起程式執行時突然崩潰

  這種結果已近是相當好了,至少你可以發現程式崩潰的位置,及時的修正問題

2、程式執行成功但是結果錯了,這種還是比較好查的分析錯誤原因費點時間

3、程式在不同的機器上執行的結果不一致查詢問題那就難上加難了

原因分析:

未初始化的變數事實上都有一個值。編譯器把該變數放到記憶體中的某個位置,而把這個位置的無論哪個位模式當做是變數初始的狀態。當被解釋成整型值時,任何為模式都是合法的值——雖然我這個值不可能是程式設計師想要的,因為這個值合法,所以使用它不可能會導致程式崩潰。可能的結果是導致程式錯誤執行或者錯誤計算。

建議:

建議每一個內建型別的物件都要初始化。雖然這樣做並不總是必須的,但是會更加容易和安全,除非你確定忽略初始化是不會帶來風險。

二、初學者誤認為巨集定義函式和普通函式一致

函式式巨集定義:#define MAX(a,b) ((a)>(b)?(a):(b))

普通函式 : MAX(a,b) { return a>b?a:b;}

兩者區別:

1、函式式巨集定義的引數沒有型別,前處理器只負責做形式上的替換,而不做引數型別檢查,所以傳參時要格外小心。

2、呼叫真正函式的程式碼和呼叫函式式巨集定義的程式碼編譯生成的指令不同。如果MAX是個普通函式,那麼它的函式體return a > b ? a : b; 要編譯生成指令,程式碼中出現的每次呼叫也要編譯生成傳參指令和call指令。而如果MAX是個函式式巨集定義,這個巨集定義本身倒不必編譯生成指令,但是程式碼中出現的每次呼叫編譯生成的指令都相當於一個函式體,而不是簡單的幾條傳參指令和call指令。所以,使用函式式巨集定義編譯生成的目標檔案會比較大。

3、函式式巨集定義要注意格式,尤其是括號。

  如果上面的函式式巨集定義寫成 #define MAX(a, b) (a>b?a:b),省去內層括號,a=1+x, b=2+y;則巨集展開就成了1+x>2+y?1+x:2+y ,運算的優先順序就錯了。同樣道理,這個巨集定義的外層括號也是不能省的。若函式中是巨集替換為 ++MAX(a,b),則巨集展開就成了 ++(a)>(b)?(a):(b),運算優先順序也是錯了。

4、若函式引數為表示式,則普通函式的呼叫與函式式巨集定義的替換過程是不一樣的。

  普通函式呼叫時先求實參表示式的值再傳給形參,例如MAX(++a, ++b),如果MAX是普通函式,a和b只增加一次。但如果MAX函式式巨集定義,則要展開成k = ((++a)>(++b)?(++a):(++b)),a和b就不一定是增加一次還是兩次了。所以若引數是表示式,替換函式式巨集定義時一定要仔細看好。

總結:

儘管函式式巨集定義和普通函式相比有很多缺點,但只要小心使用還是會顯著提高程式碼的執行效率,畢竟省去了分配和釋放棧幀、傳參、傳返回值等一系列工作,因此那些簡短並且被頻繁呼叫的函式經常用函式式巨集定義來代替實現。

如果還覺得回答不夠徹底就直接加群問小編或者在公眾號直接留言。有想讓小編總結的話題,都可以直接留言

課後小作業:

#define f(a, b, x)  a*x+b

printf("%d, %dn", f(3,2,1), f(6, 5, f(3, 2, 1)));