引用注明>> 【作者:张佩】【原文:http://blog.csdn.net/blog_index】
国庆假期我看WDK 8.1中的sample项目,遇一极有趣问题,和基本的指针使用有关,特缀文于此。请看下面是WDK8.1 msplot项目中的一段代码(经我简化过),三行而已,作一个减法运算。注释中的例值,其期望结果应是0x10,但意外得到一个溢出后的大值:
/* struct _PLOTGPC */ /* { */ /* //... */ /* LPVOID pData; //偏移为0x10 */ /* }; */ LPBYTE *pByte = pPlotGPC->pData; // pPlotGPC->pData:0x0040fa30 pByte -= (ULONG_PTR)pPlotGPC; // pPlotGPC:0x0040fa20 pPlotGPC->pData = pByte; // 结果:0xff3d11b0。期望的结果应该是0x10。
pData原来指向位于紧跟在结构体后面的一块内存,是一绝对地址;现在要把pData改成相对地址,即相对于结构体头的偏移。只要把当前值减去结构体起始地址即可。但结果很令人诧异,总是得到一个溢出数。下面是一个例子:
pPlotGPC : 0x0040fa20 pPlotGPC->pData: 0x0040fa30 结果:0x0040fa30 - 0x0040fa20 = 0xff3d11b0 //不应该是0x10吗?
这个结果让我诧异是不是CPU坏了。但看过汇编后,知道了原因。
00FF107D mov ecx,dword ptr [pPlotGPC]
00FF1080 shl ecx,2 // 乘以4,以左移2实现
00FF1083 mov edx,dword ptr [ebp-0Ch]
00FF1086 sub edx,ecx
00FF1088 mov dword ptr [ebp-0Ch],edx
原来在此减法运算中,减数乃先乘以4然后再相减的:
0x0040fa30 - (0x0040fa20×4)= 0xff3d11b0 // 被减数借位相减,得到一个溢出的大值。
到底咋回事呢?从哪多出来的×4?经过分析后发现和指针的加减法息息相关。且看pByte的定义:
LPBYTE *pByte;
其类型为(LPBYTE*),LPBYTE是(BYTE*)的宏定义,故可转换成:
BYTE** pByte;
所以pByte指针所指向数据的类型为(BYTE*),乃是一个指针类型。学过C语言的都明白,指针的加减运算,乃加减其类型size的倍数。因其类型仍为一个指针(**的缘故),类型长度根据硬件平台要么4,要么8。我编译出的目标对象乃Win32,指针长度为4。这是汇编代码乘以4的原因了。
细想起来,这很像是一个笔误,解决的办法近乎玩笑,只要去掉一个“*”即可:
LPBYTE*pByte; ==> LPBYTE pByte; // 指针类型由BYTE*变为BYTE。
一旦去掉一个*号,指针所指向的数据类型变成了BYTE,而BYTE类型长度为1,就无虞了。WDK是微软的驱动开发包,里面的示例项目对驱动开发人员有根本性的指导意义。msplot项目是WDK中唯一的完整的打印机驱动(printer driver)示例。这个bug之所以能够长期存在,我想是因为有机会进行打印机驱动开发的程序员太少的缘故,没有能够及时地把这个bug公布出来。