小議如何改變指標的指向

來源:互聯網
上載者:User

//=====================================================================
//TITLE:
//    小議如何改變指標的指向
//AUTHOR:
//    norains
//DATE:
//    Sunday  4-Semtember-2011
//Environment:
//    Visual Studio 2010
//=====================================================================

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

void Func_1(DWORD **ppdw){ *ppdw = &g_dwVal;}

 

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

pBuf = NULL;Func_1(&pBuf);

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

void Func_2(DWORD *pdw){ *(reinterpret_cast<DWORD *>(pdw)) = reinterpret_cast<DWORD>(&g_dwVal);}

 

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

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

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

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

void Func_2(DWORD *pdw){ *(reinterpret_cast<DWORD **>(pdw)) = &g_dwVal;}

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

void Func_3(BYTE *pb){ *(reinterpret_cast<DWORD **>(pb)) = &g_dwVal;}

 

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

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

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

void Func_4(DWORD dwVal){ *(reinterpret_cast<DWORD *>(dwVal)) = reinterpret_cast<DWORD>(&g_dwVal);}

 

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

 pBuf = NULL; 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",也就是指標本身的地址。既然已經知道了指標本身的地址,那麼改變指標儲存的值還有什麼問題嗎?函數所做的,只不過是一些轉換而已。

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

DWORD *pBuf = NULL;

 

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

    所謂的null 指標,只不過是指向記憶體位址為0x0000 0000的指標而已,和別的指標並沒有任何不同,所以肯定佔用空間。

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

 DWORD **ppBuf = NULL;

 

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

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

#include "windows.h"DWORD g_dwVal = 0x100;void Func_1(DWORD **ppdw){ *ppdw = &g_dwVal;}void Func_2(DWORD *pdw){ *(reinterpret_cast<DWORD **>(pdw)) = &g_dwVal; //*(reinterpret_cast<DWORD *>(pdw)) = reinterpret_cast<DWORD>(&g_dwVal);}void Func_3(BYTE *pb){ *(reinterpret_cast<DWORD **>(pb)) = &g_dwVal;}void Func_4(DWORD dwVal){ *(reinterpret_cast<DWORD *>(dwVal)) = reinterpret_cast<DWORD>(&g_dwVal);} int _tmain(int argc, _TCHAR* argv[]){ DWORD *pBuf = NULL; pBuf = NULL; Func_1(&pBuf); pBuf = NULL; Func_2(reinterpret_cast<DWORD *>(&pBuf)); pBuf = NULL; Func_3(reinterpret_cast<BYTE *>(&pBuf)); pBuf = NULL; Func_4(reinterpret_cast<DWORD>(&pBuf));  return 0;} 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.