图片-小白之家
图片-小白之家
图片-小白之家
图片-小白之家

void指针(void指针有什么用详解)

void指针一般被称为通用指针或叫泛指针。它是C言语关于朴实地址的一种约定。当某个指针是void型指针时,所指向的目标不归于任何类型。由于void指针不归于任何类型,则不可以对其进行算术运算,比方自增,编译器不知道其自增需求添加多少。比方char*型指针,自增必定是指针指向的地址加1,short*型指针自增,则偏移2。

在C/C++中,在任意时刻都可以运用其它类型指针来替代void指针,或者用void指针来替代其他类型指针。

由这些特性就可以衍生出很多比较有用的技巧。指针的实质,是其值为一个地址,那么延伸一下:

当运用关键字void声明指针变量时,它将成为通用指针变量。任何数据类型(char,int,float等)的任何变量的地址都可以赋值给void指针变量。

对指针变量的解引证,运用直接运算符*达到意图。但是在运用空指针的情况下,需求转化指针变量以解引证。这是由于空指针没有与之关联的数据类型。编译器无法知道void指针指向的数据类型。因此,要获取由void指针指向的数据,需求运用在void指针位置内保存的正确类型的数据进行类型转化。

关于空指针的解引证,你如不信,就来看看栗子:

看到了吧,直接解引证编译不过,由于编译器蒙了。

但须留意的是:

不同的编译器对void指针处理是不一样的,如IAR,ANSIC,VC对上述都将犯错,而GNU指定“void”的算法操作与“char”一致,因此上述写法在GNU则可以编译所以做个类型转化,批改如下:

void型指针解引证须做类型指定。类型转化的时分须留意类型匹配。另外,假如函数类型可以是任意类型的指针,则需将其参数界说为void*指针,例如string.h中关于内存操作的函数集:

__EFF_NENW1NW2__ATTRIBUTESintmemcmp(constvoid*,constvoid*,size_t);__EFF_NENR1NW2R1__DEPREC_ATTRSvoid*memcpy(void*_Restrict,constvoid*_Restrict,size_t);__EFF_NENR1NW2R1__DEPREC_ATTRSvoid*memmove(void*,constvoid*,size_t);__EFF_NENR1R1__DEPREC_ATTRSvoid*memset(void*,int,size_t);非易失存储办理应用

在单片机开发中,往往需求完成数据的非易失存储。所谓非易失存储,便是数据改写后在掉电后仍然能坚持。哪些对错易失存储介质呢?比方EEPROM,FLASH等都归于非易失存储介质。

比方一个产品里面有很多各式各样的参数,且分布在各个子系统文件中。举个栗子:

/*模块A中有这样一个结构体需求非易失存储*/typedefstruct_t_paras{intlanguage;/*言语品种*/charSN[20];/*产品序列号*/}T_PARAS;T_PARASsysParas;/*模块B中有这样一个结构体需求非易失存储*/typedefstruct_t_pid{floatkp;floatki;floatkd;floatT;}T_PID;T_PIDpidParas;面临这样一个需求,要完成非易失存储,我在将底层的EEPROM/FLASH读写函数完成的基础上,将上述应用数据按照必定次序存储办理。那么更为理想的方法是什么呢?规划一个模块专门负责存储非易失数据。比方:

typedefstruct_t_nv_layout{void*pElement;/*参数地址*/intlength;/*参数长度*/}T_NV_LAYOUT;/*参数映射表*/T_NV_LAYOUTnvLayout[]={{&sysParas,sizeof(T_PARAS)},/*参数映射记载*/{&pidParas,sizeof(T_PID)},…};/*参数映射表记载条数*/#defineNV_RECORD_NUMBER(sizeof(nvLayout)/sizeof(T_NV_LAYOUT))voidnv_load(T_NV_LAYOUT*pLayout,intnvAddr,intnumber);voidnv_store(T_NV_LAYOUT*pLayout,intnvAddr,intnumber);将上述规划思维,使用UML描绘一下:

在上述基础上,咱们只需求规划硬件层笼统,即可规划出一个可行的、比较通用的NV办理子系统,这样规划出的子系统忽略了事务数据,只是将其处理为数据,并不关怀其事务含义。完成了事务逻辑与后台的阻隔解耦。做到了通用性。这儿就比较巧妙的使用了void*指针的特性。假如关于该规划思维,在进一步延伸,将底层的笼统在做一层封装,将更细节的底层完成细节阻隔笼统,比方:

笼统I2C/SPIEEPROM,将其对上层的调用接口一致,那么假如你的系统原本是存储在I2CEEPROM中,现在做一个新项目,你需求运用另外一种SPI接口的EEPROM,则只需求完成相应的底层处理函数即可。将存储介质笼统,比方是EEPROM/DATAFLASH等…….那么怎么做到底层笼统呢,咱们可以使用函数指针界说一致的接口,具体部署时,只需求将完成函数的指针赋值给对应的函数指针即可,这样就做到了接口的笼统一致。其实这便是驱动模型的一个简易雏形。
图片[1]-void指针(void指针有什么用详解)-小白之家,void指针有什么用详解,void*以界说一个指针变量,但不阐明它指向哪一种类型数据.

1.传参:通用类型

能够作为函数模板,链表等参数的通用参数。在运用时,只需求强制类型转化就能够。

2.强制类型转化

有时候由于重载等的干扰,导致需求转化成void*,来进行取地址。

例如,(void*)obj.member,就能够取到member的地址;直接&(obj.member)取到的实际上是obj的开始地址。

3.指向0的地址

(void*)0,指向全是0的地址,相当于NULL。

非void类型显式转化为void类型表达式,用于防止一些代码静态查看东西的警告。

void型的指针的详细用法?

void指针能够指向任何类型的C++数据

能够用任何类型的指针直接给void指针赋值。不过假如,假如需求将void指针的值赋给其他类型的指针,则需求进行强制类型转化

如:

inta;

int*p1=&a;

void*p2=p1;//不需求强制类型转化

int*p4=(int*)p2;//需求强制类型转化

C语言中void的效果是什么?

oid及void指针类型不甚了解,因而在运用上呈现了一些过错。本文将对void关键字的深刻含义进行解说,并

胪陈void及void指针类型的运用方法与技巧。

2.void的含义

void的字面意思是“无类型”,void*则为“无类型指针”,void*能够指向任何类型的数据。

void简直只有“注释”和约束程序的效果,由于从来没有人会界说一个void变量,让咱们试着来界说:

voida;

这行语句编译时会犯错,提示“illegaluseoftype’void’”。不过,即使voida的编译不会犯错,它也没有任何实际含义。

void真正发挥的效果在于:

(1)对函数返回的限制;

(2)对函数参数的限制。

咱们将在第三节对以上二点进行详细阐明。

众所周知,假如指针p1和p2的类型相同,那么咱们能够直接在p1和p2间互相赋值;假如p1和p2指向不同的数据类型,则有必要运用强制类型

转化运算符把赋值运算符右边的指针类型转化为左面指针的类型。

例如:

float*p1;

int*p2;

p1=p2;

其中p1=p2语句会编译犯错,提示“’=’:cannotconvertfrom’int*’to’float*’”,有必要改为:

p1=(float*)p2;

而void*则不同,任何类型的指针都能够直接赋值给它,无需进行强制类型转化:

void*p1;

int*p2;

p1=p2;

但这并不意味着,void*也能够无需强制类型转化地赋给其它类型的指针。由于“无类型”能够容纳“有类型”,而“有类型”则不能包

容“无类型”。道理很简单,咱们能够说“男人和女性都是人”,但不能说“人是男人”或许“人是女性”。下面的语句编译犯错:

void*p1;

int*p2;

p2=p1;

提示“’=’:cannotconvertfrom’void*’to’int*’”。

3.void的运用

下面给出void关键字的运用规矩:

规矩一假如函数没有返回值,那么应声明为void类型

在C语言中,凡不加返回值类型限制的函数,就会被编译器作为返回整型值处理。可是许多程序员却误以为其为void类型。例如:

add(inta,intb)

{

returna+b;

}

intmain(intargc,char*argv[])

{

printf(“2+3=%d”,add(2,3));

}

程序运转的结果为输出:

2+3=5

这阐明不加返回值阐明的函数的确为int函数。

林锐博士《高质量C/C++编程》中说到:“C++语言有很严厉的类型安全查看,不允许上述情况(指函数不加类型声明)发生”。可是编译

器并不一定这么确定,比如在VisualC++6.0中上述add函数的编译无错也无警告且运转正确,所以不能寄希望于编译器会做严厉的类型查看。

因而,为了防止混乱,咱们在编写C/C++程序时,关于任何函数都有必要一个不漏地指定其类型。假如函数没有返回值,一定要声明为void类

型。这既是程序杰出可读性的需求,也是编程规范性的要求。别的,加上void类型声明后,也能够发挥代码的“自注释”效果。代码的“自注

释”即代码能自己注释自己。

规矩二假如函数无参数,那么应声明其参数为void

在C++语言中声明一个这样的函数:

intfunction(void)

{

return1;

}

则进行下面的调用是不合法的:

function(2);

由于在C++中,函数参数为void的意思是这个函数不承受任何参数。

咱们在TurboC2.0中编译:

#include”stdio.h”

fun()

{

return1;

}

main()

{

printf(“%d”,fun(2));

getchar();

}

编译正确且输出1,这阐明,在C语言中,能够给无参数的函数传送恣意类型的参数,可是在C++编译器中编译相同的代码则会犯错。在C++

中,不能向无参数的函数传送任何参数,犯错提示“’fun’:functiondoesnottake1parameters”。

所以,无论在C仍是C++中,若函数不承受任何参数,一定要指明参数为void。

规矩三小心运用void指针类型

按照ANSI(AmericanNationalStandardsInstitute)规范,不能对void指针进行算法操作,即下列操作都是不合法的:

void*pvoid;

pvoid++;//ANSI:过错

pvoid+=1;//ANSI:过错

//ANSI规范之所以这样确定,是由于它坚持:进行算法操作的指针有必要是确定知道其指向数据类型大小的。

//例如:

int*pint;

pint++;//ANSI:正确

pint++的结果是使其增大sizeof(int)。(在VC6.0上测验是sizeof(int)的倍数)

可是大名鼎鼎的GNU(GNU’sNotUnix的缩写)则不这么确定,它指定void*的算法操作与char*一致。

因而下列语句在GNU编译器中皆正确:

pvoid++;//GNU:正确

pvoid+=1;//GNU:正确

pvoid++的执行结果是其增大了1。(在VC6.0上测验是sizeof(int)的倍数)

在实际的程序规划中,为迎合ANSI规范,并提高程序的可移植性,咱们能够这样编写完成相同功用的代码:

void*pvoid;

(char*)pvoid++;//ANSI:正确;GNU:正确

(char*)pvoid+=1;//ANSI:过错;GNU:正确

GNU和ANSI还有一些差异,总体而言,GNU较ANSI更“开放”,供给了对更多语法的支撑。可是咱们在实在规划时,仍是应该尽可能地迎合

ANSI规范。

规矩四假如函数的参数能够是恣意类型指针,那么应声明其参数为void*

典型的如内存操作函数memcpy和memset的函数原型分别为:

void*memcpy(void*dest,constvoid*src,size_tlen);

void*memset(void*buffer,intc,size_tnum);

这样,任何类型的指针都能够传入memcpy和memset中,这也实在地表现了内存操作函数的含义,由于它操作的目标仅仅是一片内存,而不

论这片内存是什么类型。假如memcpy和memset的参数类型不是void*,而是char*,那才叫真的奇怪了!这样的memcpy和memset显着不是一个

“纯粹的,脱离低级趣味的”函数!

下面的代码执行正确:

//示例:memset承受恣意类型指针

intintarray[100];

memset(intarray,0,100*sizeof(int));//将intarray清0

//示例:memcpy承受恣意类型指针

intintarray1[100],intarray2[100];

memcpy(intarray1,intarray2,100*sizeof(int));//将intarray2复制给intarray1

有趣的是,memcpy和memset函数返回的也是void*类型,规范库函数的编写者是多么地富有学识啊!

规矩五void不能代表一个实在的变量

下面代码都企图让void代表一个实在的变量,因而都是过错的代码:

voida;//过错

function(voida);//过错

void表现了一种笼统,这个世界上的变量都是“有类型”的,比如一个人不是男人便是女性(还有人妖?)。

void的呈现只是为了一种笼统的需求,假如你正确地舆解了面向目标中“笼统基类”的概念,也很简单了解void数据类型。正如不能给抽

象基类界说一个实例,咱们也不能界说一个void(让咱们类比的称void为“笼统数据类型”)变量。

4.总结

小小的void蕴藏着很丰厚的规划哲学,作为一名程序规划人员,对问题进行深一个层次的考虑必定使咱们收获颇丰。

——————————————-//下面自己收拾了点

不论什么类型的指针(void*,char*,int*,float*…)默认初始值都是0xCCCCCCCC

#include

#include

//#include

voidmain()

{

void*p1;

inta=10;

int*p2=&a;

cout<<p1<<endl;

cout<<(int)*p2<<endl;

p1=p2;

cout<<*(int*)p1<<endl;//!!!!!!!用空类型操作输出值!

cout<<(int)*p2<<endl;

}

/*输出:

0xCCCCCCCC

0x0012FF78

0x0012FF78

0x0012FF78

*/

在声明同时赋值NULL,在delete后立即设置为NULL。

在debug版别下指针默认初始值为0xCCCCCCCC,在Release版别下初始值为0x0000000A,(在我电脑上VC6.0)。关于指针假如暂时没有合适的初始化值,就应该把它置为NULL(0)。

关于好的编程习气来说,declare一个指针,则初始化为NULL,假如是类成员则在结构函数中initialize,当对指针运用delete时候,则置它为NULL.

0xCCCCCCCC只是在debug状态下VC生成的未界说过的指针值,用来提示这个指针是未被初始化的,在release状态下不会等于这个值(除非巧合)。关于指针假如暂时没有合适的初始化值,就应该把它置为NULL(0)。

© 版权声明
THE END
喜欢就支持一下吧
点赞0分享
评论 抢沙发

请登录后发表评论

    暂无评论内容

图片-小白之家
图片-小白之家