安全的「野指標」

2022-08-11 16:39:14 字數 2492 閱讀 8207

諸位有沒有見過安全的「野指標」呢?下面請看我的一段c++程式,靈感來自csdn上的一次討論。在此,我只需要c++的「類」,c++的其餘一概不需要,因此我沒有使用任何的c++標準庫,連輸出都是用printf完成的。 

#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,剩下的我不用說了——非法操作出現了。 

至於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。

野指標安全論

void function void 之所以說其危險,是因為這是一段完全合乎語法的 編譯的時候完美得一點錯誤也不會有,然而當執行到strcpy一句的時候,問題就會出現,因為在這之前,str的空間已經被delete掉了,所以strcpy當然不會成功。對於這種類似的情況,在林銳博士的書中有過介紹,稱其為...

什麼是野指標?野指標的危害?如何避免野指標?

什麼是野指標?野指標是指隨機指向一塊記憶體的指標 野指標的危害?如何避免野指標?我們要在以後養成良好的編碼習慣 1.將沒有指向的指標初始化指向null 指向null的指標不能對他的指向進行修改 2.當想給乙個指標指向的空間賦值時,一定要給這個指標分配空間 malloc 3.當空間分配完後,要檢查這個...

什麼是野指標?如何避免野指標?

野指標不是 null指標,它是隨即指向一塊記憶體的指標。野指標是很危險的,會導致記憶體洩漏,if語句對它不起作用。導致野指標的原因有兩種 1 野指標指向了一塊沒有訪問許可權的記憶體。即指標沒有初始化 2 野指標指向了乙個已經釋放的記憶體。因為野指標是因為我們的不良程式設計習慣造成的,所以我們養成良好...