void function( void )
之所以說其危險,是因為這是一段完全合乎語法的**,編譯的時候完美得一點錯誤也不會有,然而當執行到strcpy一句的時候,問題就會出現,因為在這之前,str的空間已經被delete掉了,所以strcpy當然不會成功。對於這種類似的情況,在林銳博士的書中有過介紹,稱其為「野指標」。
那麼,諸位有沒有見過安全的「野指標」呢?下面請看我的一段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這個類的。所以以上看似危險的操作其實都是可行且無誤的。
現在你明白為什麼我的「野指標」是安全的了,那麼以下我所列出的,就是在什麼情況下,我的「野指標」不安全:
在成員函式function中對成員變數m_ninteger進行操作;
將成員函式function宣告為虛函式(virtual)。
以上的兩種情況,目的就是強迫野指標使用屬於自己的東西導致不安全,比如第一種情況中操作本身的m_ninteger,第二種情況中變為虛函式的function成為了屬於物件的函式(這一點可以從sizeof看出來)。
其實,安全的野指標在實際的程式設計中是幾乎毫無用處的。我寫這一篇文章,意圖並不是像孔乙己一樣去琢磨回字有幾種寫法,而是想通過這個小例子向諸位寫明白c++的物件例項化本質,希望大家不但要明白what和how,更要明白why。李馬二零零三年二月二十日作於自宅。
關於成員函式ctestclass::function的補充說明
這個函式是乙個普通的成員函式,它在編譯器的處理下,會成為類似如下的**:
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。
安全的「野指標」
諸位有沒有見過安全的 野指標 呢?下面請看我的一段c 程式,靈感來自csdn上的一次討論。在此,我只需要c 的 類 c 的其餘一概不需要,因此我沒有使用任何的c 標準庫,連輸出都是用printf完成的。include class ctestclass ctestclass ctestclass vo...
什麼是野指標?野指標的危害?如何避免野指標?
什麼是野指標?野指標是指隨機指向一塊記憶體的指標 野指標的危害?如何避免野指標?我們要在以後養成良好的編碼習慣 1.將沒有指向的指標初始化指向null 指向null的指標不能對他的指向進行修改 2.當想給乙個指標指向的空間賦值時,一定要給這個指標分配空間 malloc 3.當空間分配完後,要檢查這個...
什麼是野指標?如何避免野指標?
野指標不是 null指標,它是隨即指向一塊記憶體的指標。野指標是很危險的,會導致記憶體洩漏,if語句對它不起作用。導致野指標的原因有兩種 1 野指標指向了一塊沒有訪問許可權的記憶體。即指標沒有初始化 2 野指標指向了乙個已經釋放的記憶體。因為野指標是因為我們的不良程式設計習慣造成的,所以我們養成良好...