error C2036:'void *' : unknown size void*和void**的区别

来源:互联网 发布:insert 添加多条数据 编辑:程序博客网 时间:2024/05/20 05:54

想必很多同志都从网上下载过SSDT HOOK驱动程序,这一份是很久之前的一份了,而且网上流行的也就这

一份,里面的RtlAPI也有过时的,企图编译,可是会出现:“‘PVOID’unknown size”这个错误,导致编译

失败。当然这个错误在自己编写的驱动中或者是应用程序中也是常见的错误,比如下面的代码就会出现这样

的问题:
    PVOID SSDTServiceBase;//windows define :typedef void* PVOID;
    ULONG Index;
    ULONG Address=(ULONG)(SSDTServiceBase+4*Index);
    咋一看,怎么会出错呢?其实,是类型单位大小未知,C\C++一个隐含的语法:“不允许对一个指向未知

尺寸类型(unknown size)的指针进行直接运算”。void* 和void都属于 未知大小类型,当然VC中对void加

以限制了,所以你用void a;申明一个未知类型变量是错误的。当然,作为由人开发的编译器和C语言,不可

能对每种情况都考虑到,这是很正常的,void*是可以用来声明变量是正确的,因为这定义了一个指针。看到

这你也许会想到,是类型不匹配导致的,其实另有其因:
    unknown size:故名思意,就是未知的大小;这里的大小是指某个指针所指向的类型的大小;由于void*

所指向的是一个void类型的(虽然不可能有这个类型的数据,因为它占用0个字节),所以系统判定,void*

指顶的地址变量unknown size;从而导致错误;
由于很多人没有装WDK,不过也可以在VC++上来演示:(本文代码均在VC++6.0 WindowsXP SP2上运行通过)

你把下面这段代码运行:
#include<iostream>
#include<windows.h>
using namespace std;
int main()
{
        void * a=(void*)(0x90000000);//nothing special
 cout<<(ULONG)(a+20000000)<<endl;
 return 0;
}
会得到一个错误:
error C2036: 'void *' : unknown size
该错误很少有人会注意,这种类型的错误在驱动编译器里会当做错误被捕获。
在运行下面这段代码:(注意修改的地方)

#include<iostream>
#include<windows.h>
using namespace std;
int main()
{
        void * a=(void*)(0x90000000);//nothing special
 cout<<(ULONG)((ULONG*)a+20000000)<<endl;
 return 0;
}

或者:

#include<iostream>
#include<windows.h>
using namespace std;
int main()
{
        ULONG* a=(ULONG*)(0x90000000);//nothing special
 cout<<(ULONG)(a+20000000)<<endl;
 return 0;
}

大家发现unknown size 的错误没有了,这就证明了我上面说的,因为ULONG*类型的地址变量指向的内存结构

单元大小是可知的(32位机为4bytes);

大家再测试一段代码:
#include<iostream>
#include<windows.h>
using namespace std;
int main()
{
        void**a=(void**)(0x90000000);//nothing special
 cout<<ULONG(a+20000000)<<endl;
 return 0;
}
大家惊奇的发现,unknown size 又没有了!这是为什么呢?
下面是我思考的void* 和void **的区别:
    昨天我问同学,地址和 地址的地址 的有什么不同吗?他想了想,说应该没有什么不同吧,其实,

我也这么认为的,因为我以前获取函数地址的时候。用“&函数名”和直接用“函数名”得到的结果是一样的

。也就是说地址的地址还是它本身,这个大家可以在VC++中验证。大家记住这样几个概念:

 1.地址的地址要么没意义,要么还是它本是(如果你直接像“&(&a)”这样写,是会发生错误的,如果对于一个函数名,用&fun来获取发现与fun的值一样,VC++6.0编译器中)

 2.指针指向地址,但是指针的地址是另外一个值

 3.指针本身是一个占有内存的长整数,在超过其运用范围的时候会自己释放(它所指向的内存需要手动释放),而地址是一个“虚值”,不占用内存;
    在32位机中,地址是一个4字节长的正整数,而指针是用来存放地址的,当我们定义一个指针,如

char*p;的时候,系统为p分配了一个4字节的内存空间,里面存放的值,实际上是一个unsigned long类型(

但是如果未转换为unsigned long ,在VC++中则还是会出错),用来标识地址;这个地址标识着一个char单

元的首地址,当然char只占有1一个字节。
    接下来,我们定义个void*p,我们知道这也是一个指针p,p本身是一个占有四字节的内存单元首地址,

但是它指向的却是void类型的,也就是无类型,如果直接用它参加运算,会导致“unknown size”的错误; 

  我们再来看看void ** P,我们可以把它差分成(void*)*p,这样也许大家都明白了,这个p是一个这样的指

针:它是一个指向void*类型的指针;而void*这个类型我们上面讨论了,实际是一个unsigned long类型;所

以我们知道,这样的类型是规定了单元大小(4字节)的。
    对于上面讨论的,我们可以总结出一点:C语言中对指向规定了SIZE的类型的指针,一律把它当成一个

ULONG(unsigned long);否则当做未知类型(如void*)。我们可以得出一个规范的写法,只要保证参与运

算的指针所指向的类型是可知的就行了,只要这样,编译器就能把它当做ULONG(unsigned long)去运算了


    我们有一种最为规范的写法:
    (ULONG)(ULONG((TYPE*)a)+20000000);//TYPE为C语言知道单位大小的类型如int,char ,bool等等等
    当然去掉第二个ULONG也是对的,因为这只是强制转换了一下ULONG,相当于(ULONG)(ULONG),我之所以说

它最规范,是因为,我们实际给他的是一个地址类型“(TYPE*)a”,根据标准的C语法,强制转换一下便于理

解。
    还有一点就是,直接强制转化为ULONG也是对的,因为这是个未知类型的32位正整数,所以强制转换也可

以用ULONG;当然用其他的类型不会报错,但会导致数据截断等错误。

原创粉丝点击