野指標的定義:「野指標」不是null指標,是指向「垃圾」記憶體(不可用記憶體)的指標。人們一般不會錯用null指標,因為用if語句很容易判斷。但是「野指標」是很危險的,if無法判斷乙個指標是正常指標還是「野指標」。有個良好的程式設計習慣是避免「野指標」的唯一方法。
野指標形成的成因:
一、指標變數沒有被初始化。任何指標變數剛被建立時不會自動成為null指標,它的預設值是隨機的,它會亂指一氣。所以,指標變數在建立的同時應當被初始化,要麼將指標設定為null,要麼讓它指向合法的記憶體。
二、指標p被free或者delete之後,沒有置為null,讓人誤以為p是個合法的指標。別看free和delete的名字(尤其是delete),它們只是把指標所指的記憶體給釋放掉,但並沒有把指標本身乾掉。通常會用語句if (p != null)進行防錯處理。很遺憾,此時if語句起不到防錯作用,因為即便p不是null指標,它也不指向合法的記憶體塊。例:
#include
#include
#include
int main(void)
int i;
char *p = (char *) malloc(100);
strcpy(p, "hello");
free(p); // p 所指的記憶體被釋放,但是p所指的位址仍然不變,原來的記憶體變為「垃圾」記憶體(不可用記憶體
if(p != null) // 沒有起到防錯作用
strcpy(p, "world");
for(i=0;i<5;i++) //i=5後為亂碼
printf("%c",*(p+i));
printf("\n");
另外乙個要注意的問題:不要返回指向棧記憶體的指標或引用,因為棧內存在函式結束時會被釋放。
三、指標操作超越了變數的作用範圍。這種情況讓人防不勝防,示例程式如下:
class a
public:
void func(void)
class b
public:
a *p;
void test(void)
a a;
p = &a; // 注意 a 的生命期 ,只在這個函式test中,而不是整個class b
void test1()
p->func(); // p 是「野指標」
函式 test1 在執行語句 p->func()時,物件 a 已經消失,而 p 是指向 a 的,所以 p 就成了「野指標」 。(注:這個例項我在vc下執行了一下卻沒有問題,可以正常輸出,不知道**出了問題)
其實這就是c++的神奇和複雜之處。
看下面一段**
#include
class ctestclass
; ctestclass::ctestclass( void )
void ctestclass::function( void )
void main( void )
ok,程式到此為止,諸位可以編譯執行一下看看結果如何。你也許會驚異地發現:沒有任何的出錯資訊,螢幕上竟然乖乖地出現了這麼一行字串:
this is a test function.
奇怪嗎?不要急,還有更奇怪的呢,你可以把主函式中加上一句更不可理喻的:
((ctestclass*)null)->function();
這仍然沒有問題!!
我這還有呢,哈哈。現在你在主函式中這麼寫,倘說上一句不可理喻,那麼以下可以叫做無法無天了:
int i = 888;
ctestclass* p2 = (ctestclass*)&i;
p2->function();
你看到了什麼?是的,「this is a test function.」如約而至,沒有任何的錯誤。
你也許要問為什麼,但是在我解答你之前,請你在主函式中加入如下**:
printf( "%d, %d", sizeof( ctestclass ), sizeof( int ) );
這時你就會看到真相了:輸出結果是——得到的兩個十進位制數相等。對,由sizeof得到的ctestclass的大小其實就是它的成員m_ninteger的大小。亦即是說,對於ctestclass的乙個例項化的物件(設為a)而言,只有a.m_ninteger是屬於a這個物件的,而a.function()卻是屬於ctestclass這個類的。所以以上看似危險的操作其實都是可行且無誤的。
現在你明白為什麼我的「野指標」是安全的了,那麼以下我所列出的,就是在什麼情況下,我的「野指標」不安全:
(1)在成員函式function中對成員變數m_ninteger進行操作;
(2)將成員函式function宣告為虛函式(virtual)。
以上的兩種情況,目的就是強迫野指標使用屬於自己的東西導致不安全,比如第一種情況中操作本身的m_ninteger,第二種情況中變為虛函式的function成為了屬於物件的函式(這一點可以從sizeof看出來)。
其實,安全的野指標在實際的程式設計中是幾乎毫無用處的。我寫這一篇文章,意圖並不是像孔乙己一樣去琢磨回字有幾種寫法,而是想通過這個小例子向諸位寫明白c++的物件例項化本質,希望大家不但要明白what和how,更要明白why。李馬二零零三年二月二十日作於自宅。
關於成員函式ctestclass::function的補充說明 :
(1)這個函式是乙個普通的成員函式,它在編譯器的處理下,會成為類似如下的**:
void function( const ctestclass * this ) // ①
那麼p->function();一句將被編譯器解釋為:
function( p );
這就是說,普通的成員函式必須經由乙個物件來呼叫(經由this指標啟用②)。那麼由上例的delete之後,p指標將會指向乙個無效的位址,然而p本身是乙個有效的變數,因此編譯能夠通過。並且在編譯通過之後,由於ctestclass::function的函式體內並未對這個傳入的this指標進行任何的操作,所以在這裡,「野指標」便成了乙個看似安全的東西。
然而若這樣改寫ctestclass::function:
void ctestclass::function( void )
那麼它將會被編譯器解釋為:
void function( const ctestclass * this )
你看到了,在p->function();的時候,系統將會嘗試在傳入的這個無效位址中尋找m_ninteger成員並將其賦值為0,剩下的我不用說了——非法操作出現了。
(問題出來了,我在vc上執行時,卻可以對m_integer進行更改,不知道這是問什麼)。
至於virtual虛函式,如果在類定義之中將ctestclass宣告為虛函式:
class ctestclass
; 那麼c++在構建ctestclass類的物件模型時,將會為之分配乙個虛函式表vptr(可以從sizeof看出來)。vptr是乙個指標,它指向乙個函式指標的陣列,陣列中的成員即是在ctestclass中宣告的所有虛函式。在呼叫虛函式的時候,必須經由這個vptr,這也就是為什麼虛函式較之普通成員函式要消耗一些成本的緣故。以本例而言,p->function();一句將被編譯器解釋為:
(*p->vptr[1])( p ); // 呼叫vptr表中索引號為1的函式(即function)③
上面的**已經說明了,如果p指向乙個無效的位址,那麼必然會有非法操作。
備註:
①關於函式的命名,我採用了原名而沒有變化。事實上編譯器為了避免函式過載造成的重名情況,會對函式的名字進行處理,使之成為獨一無二的名稱。
②將成員函式宣告為static,可以使成員函式不經由this指標便可呼叫。
③vptr表中,索引號0為類的type_info。
討論三:
什麼是野指標?如何避免野指標?
野指標不是 null指標,它是隨即指向一塊記憶體的指標。野指標是很危險的,會導致記憶體洩漏,if語句對它不起作用。導致野指標的原因有兩種 1 野指標指向了一塊沒有訪問許可權的記憶體。即指標沒有初始化 2 野指標指向了乙個已經釋放的記憶體。因為野指標是因為我們的不良程式設計習慣造成的,所以我們養成良好...
什麼是野指標?野指標的危害?如何避免野指標?
什麼是野指標?野指標是指隨機指向一塊記憶體的指標 野指標的危害?如何避免野指標?我們要在以後養成良好的編碼習慣 1.將沒有指向的指標初始化指向null 指向null的指標不能對他的指向進行修改 2.當想給乙個指標指向的空間賦值時,一定要給這個指標分配空間 malloc 3.當空間分配完後,要檢查這個...
野指標是什麼
野指標問題 神馬是野指標?來的?有什麼危害?1 野指標,就是指標指向的位置是不可知的 隨機的 不正確的 沒有明確限制的 2 野指標很可能觸發執行時段錯誤 sgmentation fault 3 因為指標變數在定義時如果未初始化,值也是隨機的。指標變數的值其實就是別的變數 指標所指向的那個變數 的位址...