万能指针void*学习
1)可以用任何类型的指针对 void 指针进行赋值
由于void 指针没有特定的类型,因此可以指向任何类型的数据。因此他可以指向任何类型的数据。也就是说,任何类型的指针都可以直接赋值给 void 指针,而无需进行其他相关的强制类型转换。
例如:
double d = 1.54;
void * p = & d;
2)void 指针不可以直接赋值给其它类型的指针
因为 “空类型” 可以包容 “有类型”,而 “ 有类型” 则不能包容 “空类型”。
由此可见,要将 void 指针赋值给其他类型的指针,必须进行强制类型转换。如:
1 void * p1;
2 int * p2;
3 . . .
4 p2 = (int *)p1;
3)避免对 void 指针进行算术操作
ANSI C 标准规定,进行算法操作的指针必须确定知道其指向数据类型大小,也就是说必须知道内存目的地址的确切值。
1 char a[20]="qwertyuiopasdfghjkl";
2 int *p=(int *)a;
3 p++;
4 printf("%s", p); // tyuiopasdfghjkl
p++;一步前进了sizeof(int)=4 个字节,而类型为char,sizeof(char)=1
对于 void 指针,编译器并不知道所指对象的大小,所以对 void 指针进行算术操作都是不合法的,如下面的示例代码所示:
1 void * p;
2 p++; // ANSI:错误
3 p+= 1; // ANSI:错误
但值得注意的是,GNU 则不这么认为,它指定“void*”的算法操作与“char*”一致。因此下列语句在 GNU 编译器中都是正确的:
1 void * p;
2 p++; // GUN:正确
3 p+=1; // GUN:正确
下面代码演示了在 GCC中 执行对 void 指针的自增操作:
1 #include <stdio.h>
2 int main(void)
3 {
4 void * p="ILoveC";
5 p++;
6 printf("%s\n", p); //输出:LoveC
7 }
由此可见,GNU 和 ANSI 还存在着一些区别,相比之下,GNU 较 ANSI 更“开放”,提供了对更多语法的支持。但是在真实的设计环境中,还是应该尽可能符合 ANSI 标准,尽量避免对 void 指针进行算术操作。
4)应用:内存操作函数
根据 1)中的内容,void 指针可以指向任意类型的数据,同时任何类型的指针都可以直接赋值给 void 指针,而无需进行其他相关的强制类型转换。因此,在编程中,如果函数的参数可以是任意类型指针,那么应该使用 void 指针作为函数的形参,这样函数就可以接受任意数据类型的指针作为参数。
1 void *memset(void *buffer, int b, size_t size)
2 {
3 assert(buffer!=NULL);
4 char* retAddr = (char*)buffer;
5 while (size-- > 0)
6 {
7 *(retAddr++) = (char)b;
8 }
9 return retAddr;
10 }
11
12
13 void *memcpy (void *dst, const void *src, size_t size)
14 {
15 assert((dst!=NULL) && (src!=NULL));
16 char *temp_dest = (char *)dst;
17 char *temp_src = (char *)src;
18 char* retAddr = temp_dest;
19 size_t i = 0;
20 /* 解决数据区重叠问题*/
21 if ((retAddr>temp_src) && (retAddr<(temp_src+size)))
22 {
23 for (i=size-1; i>=0; i--)
24 {
25 *(temp_dest++) = *(temp_src++);
26 }
27 }
28 else
29 {
30 for (i=0; i<size; i++)
31 {
32 *(temp_dest++) = *(temp_src++);
33 }
34 }
35 *(retAddr+size)='\0';
36 return retAddr;
37 }