C++基础入门 – void *与void是什么呢

void关键字

我们先从void谈起。

在C++中,void字面意思为无类型,即为不确定类型——类型不确定从而所占内存不确定。跟auto又不一样,像void a= 10;的定义是错误的,即void类型不能用来定义值。这行语句编译时会出错,提示“illegal use of type 'void'”。不过,即使void a的编译不会出错,它也没有任何实际意义。

void真正的作用在于:

  1. 对函数参数的限定。
    函数的参数填void,表示函数不需要参数——当然现在没什么人会这么写了(直接空着就等于void):int func(void);
  2. 对函数返回的限定——不能有任何返回值——这就是我们常写的void作返回值的函数。
    void func(int a,int b)
        {
            //函数体代码
            return;
        }

另外void*可以表示接受任意类型的指针

注意:

  1. 如果函数没有返回值,那么应声明为void类型;
    在C语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。但是许多程序员却误以为是void类型。因此为了避免混乱,我们在编写C/C++时,任何函数都应该指定其类型。如果函数没有返回值,也一定要声明为void关键字。
  2. 如果函数无参数,那么应声明其参数为void;
  3. 如果函数的参数可以是任意类型指针,那么应声明其参数为void * ;
  4. void不能代表一个真实的变量;

void*关键词

void*则不同,编译器会允许你做类似于int someInt = 10; void* par = &someInt;之类的操作,因为无论指向什么类型的指针,指针所存的地址字节大小是一样的,本身所占空间是一定的。可以认为void*就是一个可以指向任意类型的指针。在一些时候我们不需要知道void*具体类型,我们直接对内存进行操作。在这点上有点像C#中的泛型,但又不太一样,在需要操作内存时候,void*相比泛型是更为轻量化的实现方法。当然,void*在使用时也会不可避免的带来许多限制。 

如果函数的参数可以是任意类型指针,那么应声明其参数为void * 

如典型的内存操作函数memcpy和memset的函数原型分别为:
void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num );

这样,任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。如果memcpy和memset的参数类型不是void *,而是一个具体的指针类型,比如char *,那明显memcpy和memset就仅仅能对char*类型的指针进行操作。这依赖了具体的类型,就意味着我们函数只用来操作唯一且确定的类型。在上面的内存操作函数中,显然是不可取的,这只会增加冗余的信息。

//示例:memset接受任意类型指针
int intarray[100];
memset ( intarray, 0, 100*sizeof(int) ); //将intarray清0

//示例:memcpy接受任意类型指针
int intarray1[100], intarray2[100];
memcpy ( intarray1, intarray2, 100*sizeof(int) ); //将intarray2拷贝给intarray1

注意:

 

  1. 不能对void*指针直接解引用(*p),需要显式转换成其他类型的指针
    比如,下面对aa解引用,报错:表达式必须是指向完整对象类型的指针。

    C++基础入门 – void *与void是什么呢
    所以解引用之前,我们需要声明aa的具体类型,即强转为具体类型的指针,让编译器知道。
    cout<<"void*变量 解引用:"<<*(char*)aa<<endl;
  2. 把void*指针赋值给其他类型的指针需要显式转换对应具体类型的指针
    其实这个意思跟第一条并无差别,就是说我们要对一个指针进行具体操作,需要一个完整对象类型的指针。
    char *wholePin =(char*)aa;//正确写法

内存危险

众所周知,C++的指针很灵活,但同时它也是一把双刃剑,极考验程序员的经验与精力,如果用的不规范,则容易出现各种问题,严重时甚至会让项目崩盘。在使用指针时,最容易出现的问题就是内存泄漏,造成泄漏的原因主要是程序要在使用完指针之后没有手动释放。或因为一些异常原因,释放没有得到执行!这是危险的!然而在void*中存在一个隐性的概念,若想正确删除掉void*指向的动态类型变量,需要进行强制类型转换,否则不会释放原类型的内存空间!导致内存泄漏。

为什么会出现内存泄漏呢?在delete的过程中,编译器并不知道该void*指向的什么类型的变量,自然无法正确调用原类型的析构函数,因而只是调用void*内存空间下的析构函数,简单地清空了void*指针

delete voidPointer; 					//只是清空了一个指针
delete (FrameInfo*) voidPointer; 		//正确析构voidPointer指向的变量。

 

 

本章部分内容引用了以下文章:

同时在这里要特别感谢这些作者的见解分享,笔者收获颇多。

C++干货系列——起底万能指针void* - 知乎

C++之void是什么? - keep_smile-resonate - 博客园

    给TA打赏
    共{{data.count}}人
    人已打赏
    开发

    Python集合应用题-分析每队活动投票情况

    2023-9-15 9:43:11

    开发

    C++语言基础入门(3) – 程序流程结构

    2023-9-15 9:56:47

    0 条回复 A文章作者 M管理员
      暂无讨论,说说你的看法吧
    个人中心
    购物车
    优惠劵
    今日签到
    有新私信 私信列表
    搜索