• 小议如何改变指针的指向


    //=====================================================================
    //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;
    }
    
     
    


     

  • 相关阅读:
    入侵特斯拉——智能汽车安全性分析
    D-Link系列路由器漏洞挖掘入门
    工控安全入门之 Ethernet/IP
    浅谈JS数据类型存储问题
    【备忘】12306购票必杀技
    制作炫酷的专题页面
    杂记(下)
    杂记(上)
    跨域请求解决方案
    好用的表单验证插件
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6168335.html
Copyright © 2020-2023  润新知