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等…….那么怎么做到底层笼统呢,咱们可以使用函数指针界说一致的接口,具体部署时,只需求将完成函数的指针赋值给对应的函数指针即可,这样就做到了接口的笼统一致。其实这便是驱动模型的一个简易雏形。
,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)。
Tips:本站所有资源均收集自互联网,分享目的仅供学习参考,资源版权归该资源的合法拥有者所有。
Tips:若本站所发布的资源侵犯到您的合法权益,请及时联系 hqteam@qq.com 删除!
暂无评论内容