如何理解C++中的void*

这篇具有很好参考价值的文章主要介绍了如何理解C++中的void*。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.什么是void*

首先void*中的void代表一个任意的数据类型,"星号"代表一个指针,所以其就是一个任意数据类型的指针。

其实就是一个未指定跳跃力的指针

那void*的跳跃力又什么时候指定?在需要使用的时候指定就可以了,好处:可以实现泛型编程,节省代码

对于指定数据类型的指针如int* ,double*等,他们的sizeof都是4个字节,因为都是一个指针,只是指针指向的数据类型不一致。

2.void*使用场景

      2.1:当函数传参是个指针时,不确定数据类型时或者支持多种数据类型传递时。

      2.2:函数返回值不需要考虑类型,只关心返回的大小。

     

 3.void*使用中的注意点:

  1.使用赋值运算符“=”时,void*只能作为左值不能作为右值。

void*作为一个未指定数据类型的指针,可以指向任何一个数据类型的指针,但是有数据类型的指针,不能指向一个void* 的指针。

int i = 5;
int* pi = &i;
void* pv = pi;
int* pi1 = pv;//编译错误,void*类型的指针不能初始化为指定类型的指针
 

 2.void*类型必须强转为指定类型的数据才能使用。

 void*在未指定类型的情况下,是不能直接使用的,只有在转换为显示类型后才能使用

void*一定要强转为具体指针类型后才能使用. 没有强转的void*是没有意义的。

 int i = 5;
    int* pi = &i;
    void* pv = pi;
     //cout << *pv << endl;//表达式必须是指向完整对象类型的指针
    int*  pip =  (int*)pv;
    cout << " *pip="<<*pip << endl;  // *pip=5

3. 使用(void*)0表示空指针。

  在C语言中空指针定义方式:

  在C语言中NULL代表(void*)0,

#define NULL ((void*)0)

在C++语言中:

在C++中NULL代表的是0,使用nullptr来表示(void*)0空指针,

所以在C++中推荐使用新标准的nullptr来初始化一个空指针。

#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else 
        #define NULL ((void*)0)
    #endif
#endif
 

4.当void*作为函数的参数类型或者返回值类型时,说明该函数可以接收或者返回任意类型的指针。 

	/*
 void* pVoid 可以使用任意类型的实参指针类型
 返回值也可以返回任意类型的指针
  // 但是 niubiMeth()函数返回值      最终需要转换为具体类型指针才能使用。
 */
void* niubiMeth(void* pVoid) {
    return pVoid;
}


	 double  dou = 11.11;
    double* dp = &dou;
     // 但是 niubiMeth()函数返回值      最终需要转换为具体类型指针才能使用。
    double* resultPDou = static_cast<double *>(niubiMeth(dp));
    double  resultDou = *resultPDou;
    cout <<  "返回的结果 resultPDou ="<<resultPDou<<"  对应的数值resultDou ="<<resultDou << endl;
   //         返回的结果 resultPDou =0x99fec0  对应的数值resultDou =11.11
	
	

  

void*在C++中的作用其实就是为了实现泛型编程,和Java中使用Object来表示是一样的,所以又称为通用指针和泛指针,不过C++中大部分情况下会使用模板编程来实现泛型

	
	//C++中大部分情况下会使用模板编程来实现泛型。
template<typename  T>
T _say(T t) {
    return t;
}
	
int main(){   
  /*
       模板编程不需要将强制转换为具体类型
       未强转也可以直接得出结果,这是因为模板编程会在编译器帮我们生成具体的函数。
       T _say(T t) {return t;}  ->  int* _say(int* t) {return t;}
       */
     int i = 5;
	 int* pi = &i;
     int* resultPI = _say(pi);
     cout << " *resultPI="<<*resultPI<< endl; // *resultPI=5

 float ff = 10.8;
    float* pff = &ff;
    float* resultPff = _say(pff);
    cout << " *resultPff ="<<*resultPff<< endl; //  *resultPff =10.8

}

总结

1.void*是一个过渡型的指针状态,可以代表任意类型的指针,取值的时候需要转换为具体类型才能取值。其是处于数据类型顶端的状态:

2.void* 使用赋值运算符“=”赋值时,只能将具体类型赋值给void星,不能将void*赋值给具体类型。 

3.void*一般作为参数或者返回值来实现泛型编程,但是C++中一般考虑使用模板编程来实现。 文章来源地址https://www.toymoban.com/news/detail-702360.html

#include <iostream>
using namespace std;


void say(int type, void* pVoid) {
   switch(type){
       case 0:{
           int* pInt = static_cast<int *>(pVoid);
           cout << "转为int类型的指针 pInt=" <<pInt <<" 对应的数值 *pInt="<<*pInt<< endl; //转为int类型的指针 pInt=0x99ff1c 对应的数值 *pInt=9527
           break;
       }

       case 1:{
           float* pFloat = static_cast<float *>(pVoid);
           cout << "转为float类型的指针 pFloat=" <<pFloat <<" 对应的数值 *pFloat="<<*pFloat<< endl;  //转为float类型的指针 pFloat=0x99ff10 对应的数值 *pFloat=23.3333
           break;
       }

       case 2:{
           double* pDouble = static_cast<double *>(pVoid);
           cout << "转为double类型的指针 pDouble=" <<pDouble <<" 对应的数值 *pDouble="<<*pDouble<< endl; //转为double类型的指针 pDouble=0x99ff10 对应的数值 *pDouble=9527.54
           break;

       }
   }


 };


int say2( void* pVoid){
    int* pInt = static_cast<int *>(pVoid);
    cout << "转为int类型的指针 pInt=" <<pInt <<" 对应的数值 *pInt="<<*pInt<< endl;
    //转为int类型的指针 pInt=0x99ff1c 对应的数值 *pInt=9527
   return  *pInt-7;
}

float say22( void* pVoid){
 float* pFloat = static_cast<float *>(pVoid);
 cout << "转为float类型的指针 pFloat=" <<pFloat <<" 对应的数值 *pFloat="<<*pFloat<< endl;
 //转为float类型的指针 pFloat=0x99ff10 对应的数值 *pFloat=23.3333
 return  *pFloat+10;
}








void* say3(int type, void* pVoid) {
    switch(type){
        case 0:{
            int* pInt = static_cast<int *>(pVoid);
            cout << "转为int类型的指针 pInt=" <<pInt <<" 对应的数值 *pInt="<<*pInt<< endl; //转为int类型的指针 pInt=0x99ff1c 对应的数值 *pInt=9527
            return (void*)pInt;
            break;
        }

        case 1:{
            float* pFloat = static_cast<float *>(pVoid);
            cout << "转为float类型的指针 pFloat=" <<pFloat <<" 对应的数值 *pFloat="<<*pFloat<< endl;  //转为float类型的指针 pFloat=0x99ff10 对应的数值 *pFloat=23.3333
            return   (void*)pFloat;
            break;
        }

        case 2:{
            double* pDouble = static_cast<double *>(pVoid);
            cout << "转为double类型的指针 pDouble=" <<pDouble <<" 对应的数值 *pDouble="<<*pDouble<< endl; //转为double类型的指针 pDouble=0x99ff10 对应的数值 *pDouble=9527.54
            return   (void*)pDouble;
            break;

        }
    }
}





/*
 void* pVoid 可以使用任意类型的实参指针类型
 返回值也可以返回任意类型的指针
  // 但是 niubiMeth()函数返回值      最终需要转换为具体类型指针才能使用。
 */
void* niubiMeth(void* pVoid) {
    return pVoid;
}



//C++中大部分情况下会使用模板编程来实现泛型。
template<typename  T>
T _say(T t) {
    return t;
}


int main(){
    int number = 9527;
    say(0,&number);
    int resultInt =  say2(&number);
    cout <<  "返回的结果 resultInt="<<resultInt<< endl; //返回的结果 resultInt=9520

    int* resultPInt = static_cast<int *>(say3(0, &number));
    cout <<  "返回的结果 resultPInt="<<resultPInt<<"  对应的数值*resultPInt="<< *resultPInt << endl;  //返回的结果 resultPInt=0x99ff00  对应的数值*resultPInt=9527


    float f = 70.0/3.0;
    say(1,&f);
    float resultFloat = say22(&f);
    cout <<  "返回的结果 resultFloat="<<resultFloat<< endl;  //返回的结果 resultFloat=33.3333
    float *    resultPFloat = static_cast<float *>(say3(1, &f));
    cout <<  "返回的结果 resultPFloat ="<<resultPFloat<<"  对应的数值*resultPFloat="<< *resultPFloat << endl; //返回的结果 resultPFloat =0x99fef8  对应的数值*resultPFloat=23.3333




    double  d = 9527.54;
    say(2,&d);
    double*   resultPDouble = static_cast<double *>(say3(2, &d));
    cout <<  "返回的结果 resultPDouble ="<<resultPDouble<<"  对应的数值*resultPDouble="<< *resultPDouble << endl; //返回的结果 resultPDou =0x99fed8  对应的数值*resultPDou=11.11



    double  dou = 11.11;
    double* dp = &dou;
     // 但是 niubiMeth()函数返回值      最终需要转换为具体类型指针才能使用。
    double* resultPDou = static_cast<double *>(niubiMeth(dp));
    double  resultDou = *resultPDou;
    cout <<  "返回的结果 resultPDou ="<<resultPDou<<"  对应的数值resultDou ="<<resultDou << endl;
   //         返回的结果 resultPDou =0x99fec0  对应的数值resultDou =11.11




    int i = 5;
    int* pi = &i;
    void* pv = pi;
     //cout << *pv << endl;//表达式必须是指向完整对象类型的指针
    int*  pip =  (int*)pv;
    cout << " *pip="<<*pip << endl;  // *pip=5



      /*
       模板编程不需要将强制转换为具体类型
       未强转也可以直接得出结果,这是因为模板编程会在编译器帮我们生成具体的函数。
       T _say(T t) {return t;}  ->
       int* _say(int* t) {return t;}
       float* _say(float* t) {return t;  }
       */
     int* resultPI = _say(pi);
     cout << " *resultPI="<<*resultPI<< endl; // *resultPI=5

    float ff = 10.8;
    float* pff = &ff;
    float* resultPff = _say(pff);
    cout << " *resultPff ="<<*resultPff<< endl; //  *resultPff =10.8




    return  0;
}


到了这里,关于如何理解C++中的void*的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 深入理解C++中的堆与栈:内存管理的关键区别与实例解析

      概述: C++中,堆和栈是两种不同的内存分配方式。栈自动分配、释放内存,适用于短生命周期变量;堆需要手动管理,适用于动态分配内存,但需要显式释放以防内存泄漏。通过清晰的示例源代码,演示了它们在变量生命周期、访问方式等方面的区别。 C++中的堆(heap)和

    2024年02月22日
    浏览(53)
  • Java中的多态如何理解——详解

    🎈🎈🎈本篇文章我们主要讲解的是Java中的多态,那么什么是多态呢? 同类型的对象,执行同一个行为,会表现出不同的行为特征。 接下来让我们一起对多态进行详细地讲解。   多态的常见形式: 父类类型 对象名称 = new 子类构造器; 接口 对象名称 = new 实现类构造器;

    2023年04月12日
    浏览(30)
  • 如何理解Linux shell中的“2>&1”

    有时候我们常看到类似这样的脚本调用: 这里的21是什么意思?该如何理解? 先说结论:上面的调用表明将./test.sh的输出重定向到log.txt文件中,同时将标准错误也重定向到log.txt文件中。 (如果已经明白是什么作用,可跳过此小节) 上面到底是什么意思呢?我们来看下面的

    2024年02月07日
    浏览(10)
  • 46、如何理解Spring Boot中的Starter

    使用spring + springmvc,如果需要引入mybatis等框架,需要到xml中定义mybatis需要的bean starter就是定义一个starter的iar包,写一个@Configuration配置类、将这些bean定义在里面,然后在starter包的META-INF/spring.factories中写入该配置类,springboot会按照约定来加载该配置类 开发人员只需要将相应

    2024年02月16日
    浏览(34)
  • 如何深入理解 Node.js 中的流(Streams)

    Node.js是一个强大的允许开发人员构建可扩展和高效的应用程序。Node.js的一个关键特性是其内置对流的支持。流是Node.js中的一个基本概念,它能够实现高效的数据处理,特别是在处理大量信息或实时处理数据时。 在本文中,我们将探讨Node.js中的流概念,了解可用的不同类型

    2024年02月11日
    浏览(31)
  • 从public static void main(String[] args)看如何构造数据

    java语言中public static void main(String[] args)里面的ages有什么作用? 在Java语言中, public static void main(String[] args) 是一个特殊的方法,它是Java程序的入口点。当你运行一个Java程序时,程序会从这个方法开始执行。这个方法的参数 String[] args 是一个字符串数组,用于传递命令行参数

    2024年02月12日
    浏览(84)
  • 如何理解云计算服务中的Iaas、Paas、Saas?

    一、前言 我们了解这3个概念前,先来了解一下【云计算】这个概念,摘一段百度的解释: 云计算(cloud computing)是分布式计算的一种, 指的是通过网络“云”将巨大的数据计算处理程序分解成无数个小程序, 然后,通过多部服务器组成的系统进行处理和分析这些小程序得

    2024年02月03日
    浏览(35)
  • ChatGPT在语义理解和信息提取中的应用如何?

    ChatGPT在语义理解和信息提取领域有着广泛的应用潜力。语义理解是指对文本进行深层次的理解,包括词义、句义和篇章义等层面的理解。信息提取是指从文本中自动抽取结构化的信息,如实体、关系、事件等。ChatGPT作为一种预训练语言模型,具有丰富的语义理解和上下文感

    2024年02月15日
    浏览(29)
  • 7.2、如何理解Flink中的水位线(Watermark)

    目录 0、版本说明 1、什么是水位线? 2、水位线使用场景? 3、设计水位线主要为了解决什么问题? 4、怎样在flink中生成水位线? 4.1、自定义标记 Watermark 生成器 4.2、自定义周期性 Watermark 生成器 4.3、内置Watermark生成器 - 有序流水位线生成器 4.4、内置Watermark生成器 - 乱序流

    2024年02月08日
    浏览(33)
  • 机器学习中的分类问题:如何选择和理解性能衡量标准

    当涉及到机器学习和数据科学中的分类问题时,评估模型的性能至关重要。选择适当的性能衡量标准有助于我们了解模型的效果,并作出有根据的决策。本博客将介绍一些常用的分类问题衡量标准,以及它们在不同情境下的应用。 在机器学习中,分类问题是一类非常常见的任

    2024年02月07日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包