优秀的编程知识分享平台

网站首页 > 技术文章 正文

聊聊void*(聊聊日常电视剧40集免费西瓜)

nanyue 2024-10-26 11:21:26 技术文章 6 ℃

0. 从void说起

void*是C的产物,要说清楚void*我们先从void说起。

void即为不确定类型--类型不确定,从而所占内存不确定。因为C是强类型语言,所以void par = 10;之类的声明是万万不可的,即void类型不能声明实例对象。在C语言中,void的作用主要有以下两种:

  • 对函数返回类型的限定,利用void对象的大小不确定来限制函数不能有任何返回值。-- 这就是我们常写的void作返回值的函数。
  • 对函数参数类型的限定,当函数不允许接受参数时,必须用void来限定函数的参数:int func(void)。-- 当然现在没什么人会这么写了,直接写int func()即可。

1. void*是什么

不同于void,编译器允许你做类似于int par = 10; void* ppar = ∥之类的操作,因为无论指针指向什么类型,指针本身所占空间是一定的。我们可以认为void*就是一个通用指针,可以指向任意类型对象,因此void*常被称为“万能指针”

我们都知道,指针有两个属性:所指对象的地址、长度。但是指针只存储被指向对象的地址,长度则取决于指针的类型,编译器根据指针的类型从指针指向的地址向后寻址,不同的类型则寻址范围不同,各种类型之间没有本质区别,只是解释内存中的数据方式不同。对于int*,解引用时,会从指定地址向后寻找4个字节作为变量的存储单元;对于char*,会解析1个字节。而我们将一个void类型的指针指向一个int类型的实例,实际上是抹去了这一实例的类型信息,因此在使用时我们要在心里清楚被抹去的类型信息。因此,void*也可以被称为“无类型指针”,任何类型指针都可以转为void*,它无条件接受各种类型指针

2. 如何使用void*

既然如此,那么void*有什么用呢?其实,我们在很多接口中都能看到void*,例如

void* memcpy(void* dest, const void* src, sizt_t len); 
void *memset(void *buffer, int c, int count);

任何类型的指针都可以传入memcpy和memset中,这也真正体现了内存操作函数的意义。void*帮我们屏蔽了冗余的变量类型信息,而直接将内存暴露在我们面前。

又如

ssize_t read(int fd, void* buf, size_t count);

那么,为何要如此设计?因为对于这种通用型接口,你不知道用户的数据类型是什么,但是你必须能够处理用户的各种类型数据,因而会使用void*。void*能包容的接受各种类型的指针。也就是说,如果你期望接口能接受任何类型的参数,你可以使用void*类型。但是在具体使用的时候,你必须转换为具体的指针类型。例如,你传入接口的是int*,那么你在使用的时候就应该按照int*使用。

3. void*的约束

3.1 void*可以指向任何类型的地址,但带类型的指针不能指向void*的地址

正常来说如果两个指针类型不一样的话,两个指针变量是不可以直接赋值的。如:

int* a, float* b;
a = b; //编译错误

而任何类型的指针可以赋值给void*指针,但是反过来不可以,也就是说一个有类型的指针不能指向一个void类型的变量(哪怕此时void*变量)已经指向了一个有类型的地址。

int a = 5;
int* pa = &a;
void* pb = pa;
int* pa2 = pb; //编译错误,有类型的指针变量不能指向void*变量

3.2 void*指针只有强制类型转换以后才能正常取值

既然是无类型指针,那么就不要尝试做下面的事情:

  • 解引用
  • 算数运算

由于不知道其解引用操作的内存大小,以及算数运算操作的大小,因此它的结果是未知的。

#include <stdio.h>
 int main(void) {
          int a = 10;
          int *b = &a;
          void *c = b;
          *c;  //*(int*)c就对了
          return 0;
 }
 
 编译器警告如下:
 warning: dereferencing 'void*' pointer

所以,void*指针只有强制类型转换以后才可以正常取值与运算。

4. void*与template

对于c++而言,对于某些泛型可以实现的功能但又想找一种轻量化的实现方法,void*也不失为一种不错的选择。

毫无疑问,在可能的情况下使用void*会提高编译速度,但这并不应该成为我们大量使用void*的理由,正如前面所看到的,void*会将很多隐藏的问题埋入深坑,相比template而言,编译期就能暴露的问题被推迟到运行时,隐含bug风险显著提升。

5. 总结

void*很强大,但是一定要在合适的时候使用;同时强转很逆天,一定要注意前后的类型是否真的能正确转换。

通俗的讲,void*:

  • 这里有一片内存数据,我也不知道什么类型,给你了,你自己想怎么用怎么用吧,不过要用对奥。
  • 我这里什么类型都能处理,你给我一片内存数据就可以了。
最近发表
标签列表