小議如何改變指針的指向

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

//=====================================================================
//TITLE:
//    小议如何改变指针的指向
//AUTHOR:
//    norains
//DATE:
//    Sunday  4-Semtember-2011
//Environment:
//    Visual Studio 2010
//=====================================================================

    如何通过函数来改变传入指针的指向?想必大家第一反应就是使用指向指针的指针作为形参,也就是说会写类似于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.    

 
未經允許不得轉載:GoMCU » 小議如何改變指針的指向