基本介紹
- 中文名:懸掛指針
- 外文名:Dangling Pointer
- 領域:計算機編程
1.定義,2.出現懸掛指針的常見情形,2.1使用未初始化的指針,2.2指針所指的對象已經消亡,2.3指針釋放後之後未置空,2.4另在C語言中,realloc函式使用不當,3.如何避免懸掛指針的出現,
1.定義
指針指向非法的記憶體地址,那么這個指針就是懸掛指針,也叫野指針。意為無法正常使用的指針。
2.出現懸掛指針的常見情形
2.1使用未初始化的指針
出現懸掛指針最典型的情形就是在定義指針變數之後沒有對它進行初始化,如下面的程式。
#include <iostream>using namespace std; int main(){ int* p; cout<<*p<<endl; //編譯通過,運行時出錯}
2.2指針所指的對象已經消亡
指針指向某個對象之後,當這個對象的生命周期已經結束,對象已經消亡後,仍使用指針訪問該對象,將出現運行時錯誤。考察如下程式。
#include <iostream>using namespace std; int* retAddr(){ int num=10; return #} int main(){ int* p=NULL; p=retAddr(); cout<<&p<<endl; cout<<*p<<endl;}
以上程式編譯和運行都沒有錯誤,輸出結果如下:
最後一行,輸出的並非想像中的num的值10,因為變數num是存儲在棧空間的局部變數,離開函式超出其作用域後就會被釋放掉,因此輸出的值就是不確定的值了。
如果將cout<<&p<<endl;注釋,可以正常輸出num的值為10,把cout<<*p<<endl;放在之前也能正常輸出,因為num是局部變數,局部變數會在函式結束時被自動釋放,但是返回的是這個局部變數的副本,所以以任意形式使用一次之後,這個副本也將被釋放,又因為返回的是個地址,地址中的內容被釋放後,這個地址還留著,這個不就是個不知道指向何處的野指針么?p中保存著這個野指針的地址,自然可以使用*p往其中寫入變數,並正確輸出。
VC++和g++都是這樣,
VS會在return #處發出警告
//warning C4172: 返回局部變數或臨時變數的地址: num
但並不會在運行時報錯。
2.3指針釋放後之後未置空
指針p被free或者delete之後,沒有置為NULL,讓人誤以為p是個合法的指針。對指針進行free和delete,只是把指針所指的記憶體空間給釋放掉,但並沒有把指針本身置空,此時指針指向的就是“垃圾”記憶體。釋放後的指針應立即將指針置為NULL,防止產生懸掛指針。考察如下程式。
#include <iostream>using namespace std; int main(){ int* p=NULL; p=new int[10]; delete p; cout<<"p[0]:"<<p[0]<<endl;}
程式輸出結果是一個隨機值,因為此時的指針所指向的空間是垃圾記憶體,存放著隨機值。
2.4另在C語言中,realloc函式使用不當
1 2 3 4 5 6 7 8 9 | #include<malloc.h> void main() { char*p,*q; p=(char*)malloc(10); q=p; p=(char*)realloc(q,20); //………………………… } |
在這段程式中我們用q記錄原來的記憶體地址p。這段程式可以編譯通過,但在執行到7行時,若原記憶體後面沒有足夠空間來將原有空間擴展成一個連續的新大小,那么realloc函式會從堆中重新找一塊30位元組大小的記憶體,並把原來(通過調用malloc函式得到的)記憶體空間中的內容複製到這塊新記憶體中,此時數據發生了移動,那么q所指向(通過調用malloc函式得到的)的記憶體空間實際上已經放回到堆上了!這樣就會產生q指針的指針懸掛,此時如果再用q指針進行操作就可能發生意想不到的問題。
3.如何避免懸掛指針的出現
懸掛指針有時比較隱蔽,編譯器不能發現,為了防止懸掛指針帶來的危害,編程人員應該注意以下幾點。
(1)C++引入了引用機制,如果使用引用可以達到編程目的,就可以不必使用指針。因
為引用在定義的時候,必須初始化,所以可以避免懸掛指針的出現。
(2)如果一定要使用指針,那么需要在定義指針變數的同時對它進行初始化操作。定義時將其置位NULL或者指向一個有名變數。
(3)對指針進行free或者delete操作後,將其設定為NULL。對於使用 free 的情況,常常定義一個宏或者函式 xfree 來代替 free 置空指針:
#define xfree(x) free(x); x = NULL;
(4)在C語言中使用realloc函式時應當格外注意。