什麼是野指標?

2022-04-03 22:30:16 字數 3926 閱讀 5649

野指標的定義:「野指標」不是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 因為指標變數在定義時如果未初始化,值也是隨機的。指標變數的值其實就是別的變數 指標所指向的那個變數 的位址...