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