改變指針指向

http://blog.csdn.net/norains/article/details/6746844


   如何通過函數來改變傳入指針的指向?想必大家第一反應就是使用指向指針的指針作爲形參,也就是說會寫類似於Func_1的函數:

  1. void Func_1(DWORD **ppdw)  
  2. {  
  3.  *ppdw = &g_dwVal;  
  4. }  

 

    g_dwVal是一個全局變量,只要知道這個即可,其餘的暫時不用考慮。有了Func_1,那麼調用估計大家也會想到,無非是傳入一個指針,如:

  1. pBuf = NULL;  
  2. Func_1(&pBuf);  


    如果你認爲這是C++通過函數改變指針指向的全部,那麼你就太小看它了。使用"DWORD **"能改變指針的指向,那是不是通過"DWORD *"就不行了呢?答案是否定的,形參爲"DWORD *"也可以改變指針指向!不過,這個函數的寫法就有所不同,如Func_2:

  1. void Func_2(DWORD *pdw)  
  2. {  
  3.  *(reinterpret_cast<DWORD *>(pdw)) = reinterpret_cast<DWORD>(&g_dwVal);  
  4. }  

 

    函數寫法不同,調用也要有所區別,如:

  1. pBuf = NULL;  
  2. Func_2(reinterpret_cast<DWORD *>(&pBuf));  


    如果你測試過這段代碼,那麼會發現即使函數形參是"DWORD *",也可以改變指針的指向!

    在這裏再稍微多說一點,Func_2的函數體,其實寫成這樣也是可以正常賦值的,如:

  1. void Func_2(DWORD *pdw)  
  2. {  
  3.  *(reinterpret_cast<DWORD **>(pdw)) = &g_dwVal;  
  4. }  


    我們再來看一個更加有趣的問題,如果指針的類型是BYTE,那麼是不是也能正常改變呢?所以,我們便有了一個Func_3函數:

  1. void Func_3(BYTE *pb)  
  2. {  
  3.  *(reinterpret_cast<DWORD **>(pb)) = &g_dwVal;  
  4. }  

 

    調用的時候,自然也是有所區別:

  1. pBuf = NULL;  
  2. Func_3(reinterpret_cast<BYTE *>(&pBuf));  


    經過測試,這樣的方式也是能夠改變指針的指向的。估計看到這裏,應該不少朋友迷惑了,爲什麼呢?在回答這個問題之前,我們繼續再看另一個更有趣的問題,不通過指針,而是通過"DWORD"類型來改變指針的指向!於是,便有了函數Func_4:

  1. void Func_4(DWORD dwVal)  
  2. {  
  3.  *(reinterpret_cast<DWORD *>(dwVal)) = reinterpret_cast<DWORD>(&g_dwVal);  
  4. }  

 

     不用想,調用方式自然也是有區別,如:

  1. pBuf = NULL;  
  2. Func_4(reinterpret_cast<DWORD>(&pBuf));  


    估計很多初學者看到這裏,應該已經兩眼發暈了吧?我們不妨看看爲何可以改變的真正原因。

    要明白上述函數爲何能夠正常改變指向,那麼就必須明白指針的地址。對於指針的地址,它其實分爲兩部分,一部分是指針本身的地址,另一部分則是指針指向的地址。這樣說可能大家有點糊塗,不妨看如下的圖示:


    如果是C++高手的話,那麼對於這張圖肯定是非常熟悉,但可能初學者就有點暈了。沒事,我們現在一起來看看。


   假設有個指針,名爲pBuf。對於圖中的0x4000 0000來說,這是指針本身的地址,以代碼表示,便是&pBuf;而0x4000 0000這個內存地址存儲的0x8000 0000,便是指針指向的地址,代碼表示爲pBuf;至於0x1234 5678,不用說,就是0x8000 0000這個內存塊的數值了,代碼自然是*pBuf。根據這些內容,不難得出這個表:

地址/數值

代碼

0x4000 0000

&pBuf

0x8000 0000

pBuf

0x1234 5678

*pBuf



    還是以圖爲例子,如果要改變指針的指向的話,那麼只需要改變0x4000 0000這個內存裏面的數值即可。明白這點,對於之前的函數理解就沒什麼難度了。大家不妨回頭看看,其實在調用這些Func_x函數時,傳入的都是"&pBuf",也就是指針本身的地址。既然已經知道了指針本身的地址,那麼改變指針存儲的值還有什麼問題麼?函數所做的,只不過是一些轉換而已。

    按理說,本文到此已經結束,但最後不妨再看一個初學者非常容易搞混的問題:空指針是否佔據內存空間?也就是說,下面這行代碼是否佔據內存空間: 

  1. DWORD *pBuf = NULL;  

 

    答案是佔有空間!如果對此還有疑惑,那麼看了下面這張圖,相想必就非常明白了:


    所謂的空指針,只不過是指向內存地址爲0x0000 0000的指針而已,和別的指針並沒有任何不同,所以肯定佔用空間。

    這裏,應該還會有人迷惑,如果指向指針的指針爲空,那麼會不會佔用空間呢?也就是說下面這行代碼:

  1. DWORD **ppBuf = NULL;  

 

     答案還是佔用空間!指向指針的指針說白了,還是指針,既然是指針就有自己本身的地址,所以肯定佔用空間!所不同的是,指向指針的指針的存儲空間存儲的是所指向的指針的地址而已。如果以第一幅圖爲例子,稍微完善一下,那麼便有如下圖示:


     最後,便是全部的代碼,以供大家參考:

  1. #include "windows.h"  
  2.   
  3. DWORD g_dwVal = 0x100;  
  4.   
  5. void Func_1(DWORD **ppdw)  
  6. {  
  7.  *ppdw = &g_dwVal;  
  8. }  
  9.   
  10. void Func_2(DWORD *pdw)  
  11. {  
  12.   
  13.  *(reinterpret_cast<DWORD **>(pdw)) = &g_dwVal;  
  14.   
  15.  //*(reinterpret_cast<DWORD *>(pdw)) = reinterpret_cast<DWORD>(&g_dwVal);  
  16.   
  17. }  
  18.   
  19. void Func_3(BYTE *pb)  
  20. {  
  21.  *(reinterpret_cast<DWORD **>(pb)) = &g_dwVal;  
  22. }  
  23.   
  24. void Func_4(DWORD dwVal)  
  25. {  
  26.  *(reinterpret_cast<DWORD *>(dwVal)) = reinterpret_cast<DWORD>(&g_dwVal);  
  27. }  
  28.   
  29.    
  30.   
  31. int _tmain(int argc, _TCHAR* argv[])  
  32. {  
  33.  DWORD *pBuf = NULL;  
  34.   
  35.  pBuf = NULL;  
  36.  Func_1(&pBuf);  
  37.   
  38.  pBuf = NULL;  
  39.  Func_2(reinterpret_cast<DWORD *>(&pBuf));  
  40.   
  41.  pBuf = NULL;  
  42.  Func_3(reinterpret_cast<BYTE *>(&pBuf));  
  43.   
  44.  pBuf = NULL;  
  45.  Func_4(reinterpret_cast<DWORD>(&pBuf));  
  46.    
  47.  return 0;  
  48. }  
  49.   
  50.    

發佈了3 篇原創文章 · 獲贊 6 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章