C 語言常見誤解/指標/空指標與NULL

指標(二)空指標與 NULL

編輯

問:NULL 是空指標嗎?型態是什麼?

答:NULL 不一定是指標,但一定是空指標常數(兩者概念不同)。空指標常數轉型成指標後保證可以拿到正確的空指標,但自己不一定是指標。C99 (N1256) 中,空指標常數代表計算結果為零的整數常數表達式,如 00ULL,或是前述表達式轉型成 void*, 如 (void*)(0U).

C++03 中空指標常數只能是計算結果為零的整數常數表達式,如 0, 連指標都不是,在多載時可能會選了錯誤的函式。C++0x 引入 nullptr 企圖解決這問題。(可參考較容易閱讀的 More C++ Idioms/nullptr


問:下列程式碼一定可以拿到空指標嗎?

  int a = 0;
  int *p = (int *)a;

答:不保證。只有常數表達式才保證會轉型成空指標,執行期間才得到的零不保證可以成功轉型成空指標。一個可能的原因是,不是所有機器的空指標都是零(見此題目),如果要允許執行期間的空指標轉換,程式很可能需要在執行期間特別檢查數字是不是零. 如果是常數,編譯器可以在編譯期間就轉成適當的空指標。(參考 clc FAQ 5.18


問:所以 printf("%p", NULL) 嚴格符合標準嗎?

答:否。這樣才嚴格符合標準:

printf("%p", (void*)NULL);

注意 NULL 不是空指標,但 NULL 轉型成指標後一定是空指標。


問:如果每次用 NULL 都先轉型成適當的型態(如寫 (int*)NULL)是不是就沒有陷阱了?

答:是(雖然如果非常清楚標準的話,其實不用每個地方都寫)。這個轉換對 C++ 尤其重要,可以避免很多問題,見上方〈 NULL 是空指標嗎?〉。


問:為什麼這麼麻煩?空指標一定是零不是嗎?為什麼不能直接把 NULL 定成空指標?

答:不是所有機器的空指標(的表示法)都是零,而且不是所有指標都長得一樣(見〈指標表示法〉),所以很難定義一個萬用空指標。C99 (N1256) 中規定空指標跟空指標常數的比較結果會相等,也可以把空指標常數寫入一個指標(編譯器會在編譯期間做適當的轉換)。(參考 clc FAQ 5.17 看實際機器)


問:既然空指標不一定是零,所以寫 NULL 比寫 0 好不是嗎?因為可以應付不同的機器。

答:這無助於可攜性,比較像是寫作風格和美觀的問題(可能有助於除錯)。C99 (N1256) 中常數零一定是合法的空指標常數,一定可以成功轉型成空指標,也一定可以和其他指標比較。


問:假設全域變數寫

int *p;

既然空指標不一定是零,p 會初始化成什麼呢?

答:C99 保證會初始化成空指標(即使在空指標不是零)。(參考 N1256 6.7.8p10)