懸置指標與野指標
指標的好壞完全取決於它裡面的位址,而那個位址變壞的兩種方式各有名字。懸置指標(dangling pointer)曾經指向一個有效物件,但物件已經沒了——它被釋放了,或它是一個其函式已返回的區域變數。野指標(wild pointer)則從未指向任何有意義之處——它是一個未初始化的指標,持有它記憶體中恰好殘留的任何位元組。兩者看起來都像普通指標;兩者都是地雷。
懸置指標更陰險,因為它曾經是正確的。呼叫 free(p) 之後,那塊記憶體歸還給配置器,但 p 仍持有舊位址——那裡的位元組甚至可能有一陣子看起來沒變——所以在你重設它之前 p 都是懸置的。同樣地,區域變數的位址會在它的函式一返回、堆疊框被重用的瞬間懸置。野指標就只是你宣告了卻從未指派的指標:int *p; 然後 *p = 5; 就透過一個垃圾位址寫入。兩者共通的療方是紀律——初始化每個指標(若還沒有東西就設成 NULL),並在 free(p) 之後立刻做 p = NULL,這樣過期的使用就變成乾淨的空指標當掉,而不是釋放後使用。
這些並非理論:解參考或釋放任一種都是未定義行為,而且失敗的方式很狡猾。有時是立刻的記憶體區段錯誤;有時是悄悄讀取或毀損「現在擁有那些位元組的人」,損害在很遠之後才浮現。一個懸置或野指標不管當掉了沒,都是個臭蟲——「它看起來能跑」並不是它安全的證據。
free(p); ... *p = 1; 在釋放後使用 p——p 已懸置,這是釋放後使用。修法:free(p); p = NULL;,這樣日後的 *p 會乾淨地當掉。野指標:int *q; *q = 1; 透過一個未初始化的垃圾位址寫入。
釋放後把指標設為 NULL;絕不要使用未初始化的指標。
free() 不會改變指標、也不會抹除位元組——在你自己把它設為 NULL 之前,指標仍然懸置。一個還沒當掉的懸置或野指標並不安全;那個未定義行為可能正在悄悄毀損記憶體,並在別處浮現。