C 語言常見誤解/指標/空指標與NULL
指標(二)空指標與 NULL
編輯問:NULL
是空指標嗎?型態是什麼?
答:NULL
不一定是指標,但一定是空指標常數(兩者概念不同)。空指標常數轉型成指標後保證可以拿到正確的空指標,但自己不一定是指標。C99 (N1256) 中,空指標常數代表計算結果為零的整數常數表達式,如 0
或 0ULL
,或是前述表達式轉型成 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)