作者:守望,Linux应用开发者,目前在公众号【编程珠玑】 分享Linux/C/C++/数据结构与算法/工具等原创技术文章和学习资源。说到C就不得不提指针,而一提到指针,有一个是比较特殊的,那就是
void*。void*到底是怎样的存在?指针类型的含义
在说明void*之前,先了解一下普通指针类型的含义。//来源:公众号【编程珠玑】//main.c#include int main(void){ int a[] = {0x01020304,2019}; int *b = a; char *c = (char*)&a[0]; printf("b+1:%d\n",*(b+1)); printf("c+1:%d\n",*(c+1)); return 0;}`上面的输出结果为:b+1:2019c+1:3对于上面的结果,也许你并不感到意外。如果你的疑问是为什么不是2而是3,那么建议你看看《谈一谈字节序的问题》。同样是指针类型,b和c有什么区别?一个是指向整型的指针,一个是指向char型的指针,当它们执行算术运算时,它们的步长就是对应类型占用空间大小。即b + 1 //移动sizeof(int)字节| 04 | 03 | 02 | 01 | 2019 |
| 字节0 | 字节1 | 字节2 | 字节3 | 字节4~7 |
| ↑ |
c + 1 //移动sizeof(char)字节它的指向如下:| 04 | 03 | 02 | 01 | 2019 |
| 字节0 | 字节1 | 字节2 | 字节3 | 字节4~7 |
| ↑ |
结论
各种类型之间没有本质区别,只是解释内存中的数据方式不同。例如,对于int型指针b,解引用时,会解析4字节,算术运算时,也是以该类型占用空间大小为单位,所以b+1,移动4字节,解引用,处理4字节内容,得到2019。对于char型指针c,解引用时,会解析1个字节,算术运算时,也是以sizeof(char)为单位,所以c+1,移动一字节,解引用,处理1字节,得到03。所以像下面这样的操作:char a[] = {01,02,03,04};int *b = (int*)(a+2);如果你试图解引用b,即*b,就可能遇到无法预料的问题,因为将会访问非法内存位置。a+2,移动sizeof(char)字节,指向03,此时按照int类型指针解引用,由于int类型解引用会处理4字节内存,但是后面已经没有属于数组a的合法内容了,因此可能出错。指针占用空间大小
正由于它们没有本质区别,它们占用空间大小在同一个程序中都是固定的,对于32位程序,占用4字节空间,64位占用8字节,而正因如此,64位程序理论能使用的内存是足够大的,而32位程序理论上能使用的不过4G(2^(4*8bit)),再加上内核空间的使用,真正能用到的可能就3G左右。如果你的系统是64位的,那么默认情况下,编译出来的程序也是64位的。如果你想编译为32位,可以使用-m32参数:$ gcc -m32 -o main main.c如何确定是多少位的程序:$ readelf -h mainClass: ELF32上面的ELF32,表明了它是32位程序。或者可以看Machine字段:Machine: Intel 80386void*
说回void*,前面说了,指针的类型不过是解释数据的方式不同罢了,这样的道理也可用于很多场合的强制类型转换,例如将int类型指针转换为char型指针,并不会改变内存的实际内容,只是修改了解释方式而已。而void *是一种无类型指针,任何类型指针都可以转为void\*,它无条件接受各种类型。而既然是无类型指针,那么就不要尝试做下面的事情:- 解引用
- 算术运算
#include int main(void){ int a = 10; int *b = &a; void *c = b; *c; return 0;}编译警告如下:warning: dereferencing ‘void *’ pointer如何使用
既然如此,那么void*有什么用呢?实际上我们在很多接口中都会发现它们的参数类型都是void*,例如:ssize_t read(int fd, void *buf, size_t count);void *memcpy(void *dest, const void *src, size_t n);为何要如此设计?因为对于这种通用型接口,你不知道用户的数据类型是什么,但是你必须能够处理用户的各种类型数据,因而会使用void*。void*能包容地接受各种类型的指针。也就是说,如果你期望接口能够接受任何类型的参数,你可以使用void*类型。但是在具体使用的时候,你必须转换为具体的指针类型。例如,你传入接口的是int*,那么你在使用的时候就应该按照int*使用。注意
使用void*需要特别注意的是,你必须清楚原始传入的是什么类型,然后转换成对应类型。例如,你准备使用库函数qsort进行排序:void qsort(void *base,size_t nmemb,size_t size , int(*compar)(const void *,const void *));它的第三个参数就是比较函数,它接受的参数都是const void*,如果你的比较对象是一个结构体类型,那么你自己在实现compar函数的时候,也必须是转换为该结构体类型使用。举个例子,你要实现学生信息按照成绩比较://来源:公众号【编程珠玑】typedef struct student_tag
{ char name[STU_NAME_LEN]; //学生姓名 unsigned int id; //学生学号 int score; //学生成绩}student_t;int studentCompare(const void *stu1,const void *stu2){ /*强转成需要比较的数据结构*/ student_t *value1 = (student_t*)stu1; student_t *value2 = (student_t*)stu2; return value1->score-value2->score;}在将其传入studentCompare函数后,必须转换为其对应的类型进行处理。更多函数指针相关内容可以参考《高级指针话题-函数指针》,那里有更多的介绍。总结
void*很强大,但是一定要在合适的时候使用;同时强转很逆天,但是一定要注意前后的类型是否真的能正确转换。通俗地说void*:- 这里有一片内存数据,我也不知道什么类型,给你了,你自己想怎么用怎么用吧,不过要用对奥!
- 我这里什么类型都能处理,你给我一片内存数据就可以了
●编号634,输入编号直达本文
●输入m获取文章目录
C语言与C++编程
分享C/C++技术文
本文探讨了指针类型的含义,指出了指针占用的空间大小,并详细阐述了void*的作用。讲解了如何将函数指针转换为void*以及如何正确使用,强调了在使用过程中需要注意的事项,最后进行了总结。通过了解,可以更好地理解在QT中void*作为通用指针的使用场景。
5194

被折叠的 条评论
为什么被折叠?



