小议如何改变指针的指向

//=====================================================================
//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",也就是指针本身的地址。既然已经知道了指针本身的地址,那么改变指针存储的值还有什么问题么?函数所做的,只不过是一些转换而已。

    按理说,本文到此已经结束,但最后不妨再看一个初学者非常容易搞混的问题:空指针是否占据内存空间?也就是说,下面这行代码是否占据内存空间: 

DWORD *pBuf = 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;
}

 


 

原文地址:https://www.cnblogs.com/wangfengju/p/6172986.html