在进行c语言开发过程中,有时候会遇到void *这种数据类型。比如:
void * memcpy(void * dest, const void * src, size_t n);
void * memmove(void * dest, const void * src, size_t n);
对于指针类型来说,不管是int *,char * 还是void*,亦或是struct stru*,这种类型都是值一个地址,在32位环境下,都是一个32位的无符号整形数值,表示一个地址。既然都是一个地址,为啥还弄这么多种类型呢?
其实,对于int *a,或者char * b,抑或 void *c来说。这里的*表示这是一个指针类型,用这玩意定义的变量(如,a,b,c)都是一个地址,都是一个uint32_t类型(在32位环境下)。*前面的数据类型呢?作用有2个:(1)便于人看和记忆,让人知道这玩意定义的那个地址以及后续指定长度的数据怎么解析;(2)给编译器用的,让它知道那个地址及后面的连续的指定长度的数据如何解析。这里所谓的指定长度,就和*前面的数据类型有关啦。
对于int *a来说,a表示1个地址如0x12345678(4个字节),int表示从0x12345678开始的4个字节(siezof(int)=4)需要用int来解释,或者说那4个连续字节表示一个int类型数据;对于char* b来说,b表示0x00112345这个地址,而char表示从b开始的连续1个字节需要用char类型来解析。
这样下来,对于void * c呢?c表示一个地址0x00112233,它后面的数据怎么解析呢?有多长呢?都不知道。因为c语言没有规定void多长,占几个字节等等。所以呢?c这个变量是没法直接用的。
那你又说了,既然没法用,那这玩意就是废物,定义它干啥?
你想想C++中的基类和虚函数。明白了吗?这玩意没法用,但可以充当一个类似基类功能的变量啊。上面void *c,c表示一个4字节的地址。在32位环境下,不管是哪种数据类型,*都是表示一个32位的地址。那我定义一个void *c,是不是可以把其他任何类型赋值给它呢?你想要啥类型,就用啥类型替换void不就行了?
类似上面的memcpy,虽然参数是void *,但是你把char*放进去实例化,是可以用的,你把执行一块int区域的int*放进去,也是可以的。这样,你就不用再给每种类型的数据拷贝分别顶一个函数了。只需要一个函数,就可以实现各种数据类型的内存拷贝。
上面其实就是说了可以把void*强制转换成任意数据类型的指针,但是反过来呢?
把任意数据类型的指针强制转换成void*可以吗?例如,我定义了一个copy(int *dest,const int * src,int len)实现把src指向的存储int类型数据的区域拷贝len个数据(实际是4*len字节)到dest指向的区域。那我这时候有一个void *a, void *b。我是否可以执行copy(a,b,len)呢?
显然是不行的!为啥?对于b来说,b开始的连续区域,单位是多少呢?虽然要拷贝len个数据,但是每个数据多长呢?没法确定。所以无法执行。