void类型的指针(即void*)一般用法和注意事项
一、void类型指针使用
1、void 指针是一种特殊的指针,表示为“无类型指针”,由于 void 指针没有特定的类型,因此它可以指向任何类型的数据,也可以称为通用类型指针。也就是说,任何类型的指针都可以直接赋值给 void 指针,而无需进行其他相关的强制类型转换
比如以下代码:
int a = 10; //创建一个变量
int* pa = &a; //指针pa指向a,pa指针的类型是int
void* s = pa; //直接可以将pa赋给s,不需要强制类型转换
void* s = &a; //也可以直接取a的地址赋值给s
通过上述代码,你只需要知道任何类型的指针都可以直接不加转换的赋值给void指针
2、我们知道了如何给void类型指针赋值,我们也要知道如何使用void类型指针。void类型是不能直接解引用访问所指内存空间以及不能直接进行算术运算,是需要强制转换成其他具体类型才能使用
比如以下代码:
//我们继续使用上面的代码
int a = 10; //创建一个变量
int* pa = &a; //指针pa指向a,pa指针的类型是int
void* s = pa; //直接可以将pa赋给s,不需要强制类型转换
//使用void类型指针
void* s1 = s; //void类型指针可以直接赋给void类型指针
int* p1 = (int*)s; //要想将s赋值给int类型指针,需要加强制类型转换
printf("a = %d\n", *(int*)s); //通过void类型指针访问a
//void类型指针使用注意事项
//void类型指针不能直接进项算术运算
s++; //错误,不能自增
s = s + 1; //错误,不能加1,因为你没有类型,加1的话,编译器根本不知道加几个字节
s = (int*)s + 1; //正确,必须先转换成具体类型,再计算
void* s2 = NULL;
if (s2 < s1) //正确,虽然void类型指针不可以算术运算,但是可以关系运算
printf("s2小\n");
else
printf("s1小\n");
二、为什么有void类型指针
1、我们知道上面两条就已经够用了,但是为什么要有void类型指针?
因为当要接收任何类型指针的时候可以用void类型指针作形参、当不知道返回什么类型指针的时候可以返回void类型指针
请看下面例子:
#include <stdio.h>
//交换两个整形
void swap_int(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//交换两个短整型
void swap_short(short* p1, short* p2)
{
short tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//交换两个字符
void swap_char(char* c1, char* c2)
{
char tmp = *c1;
*c1 = *c2;
*c2 = tmp;
}
int main()
{
int a = 10;
int b = 20;
swap_int(&a, &b); //交换a和b
printf("a = %d, b = %d\n", a, b);
short c = 10;
short d = 20;
swap_int(&c, &d); //交换c和d
printf("c = %d, d = %d\n", c, d);
char e = '1';
char f = '2';
swap_char(&e, &f); //交换两个字符
printf("e = %c, f = %c\n", e, f);
}
上面代码通过函数swap_int交换两个整形,通过swap_short交换两个短整型,通过swap_char交换两个字符,但是我们有没有发现这三个函数的参数个数一样,功能一样,代码内部实现也一样,只不过是参数的类型不一样,那我们可不可以将三个函数合成一个函数,这样一来可以减少代码的重复?
答案是可以的,但是如何设计函数形参和函数体?
首先:我们要交换不同类型的数据,就要传入不同类型的指针,我该用一个什么样的指针接受不同类型的指针,此时就可以用void类型指针,我们都知道,void类型指针可以接受任何类型指针,并且不需要强制类型转换,所以函数形参应该用两个void类型的指针来接收传入的数据,并且还应该再有一个形参num,表示要交换的数据所占的字节数,为什么要有这个参数,请看下面
其次:函数体的设计,交换数据应该使用字节一个一个交换,因为任何类型的数据在内存中存储的最小单位是字节,也就是说无论你数据多大、数据有多少种不同的类型,但是终究要把你拆分成字节存放在内存中,此时交换时候就很方便,我就不用考虑你传入的是什么类型的数据,我直接按照字节一个一个的交换就行,但是前提条件就是你要给我传入要拷贝的字节数,这也就是为什么形参中要传入字节数了
改进代码如下:
#include <stdio.h>
//交换任何数据类型的数据
void swap(void* p1, void* p2, size_t num)
{
size_t i = 0;
for (i = 0; i < num; i++)
{
char tmp = *((char*)p1 + i);
*((char*)p1 + i) = *((char*)p2 + i);
*((char*)p2 + i) = tmp;
}
}
int main()
{
int a = 10;
int b = 20;
swap(&a, &b, sizeof(a)); //交换两个整形
printf("a = %d, b = %d\n", a, b);
char c = 'a';
char d = 'b';
swap(&c, &d, sizeof(c)); //交换两个字符
printf("c = %c, d = %c\n", c, d);
return 0;
}
总结:通过这个例子我们就可以知道为什么要有void类型指针,以及它的使用场景,标准库函数memcpy和memmove中也使用到了,如果想要模拟实现可以参考:memcpy和memmove模拟实现