C++编程中指针的声明与基本使用讲解
使用以下序列声明指针。
[storage-class-specifiers][cv-qualifiers]type-specifiers [ms-modifier]declarator;
其中,任何有效指针声明符均可用于declarator。简单指针声明符的语法如下所示:
*[cv-qualifiers]identifier[=expression]
1.声明说明符:
可选存储类说明符。
应用于要指向的对象的类型的可选const或volatile关键字。
类型说明符:可表示要指向的对象的类型的类型名称。
2.声明符:
可选的Microsoft专用修饰符。
*运算符。
应用于指针本身的可选const或volatile关键字。
标识符。
可选初始值设定项。
指向函数的指针的声明符类似于以下形式:
(*[cv-qualifiers]identifier)(argument-list)[cv-qualifers] [exceptionspecification][=expression];
对于指针数组,语法如下所示:
*identifier[[constant-expression]]
但是,指针声明符可能更复杂。
多个声明符及其初始值设定项可能同时出现在前面有声明说明符且以逗号分隔的列表中的一个声明中。
指针声明的简单示例如下:
char*pch;
前面的声明指定pch指向char类型的对象。
更复杂的示例是
staticunsignedint*constptr;
前面的声明指定ptr是一个指向unsignedint类型(带静态存储持续时间)的对象的常量指针。
下一个示例演示如何声明和初始化多个指针:
staticint*p=&i,*q=&j;
在前面的示例中,指针p和q都指向类型int的对象并分别初始化为i和j的地址。存储类说明符static应用于这两个指针。
//pointer.cpp //compilewith:/EHsc #include<iostream> intmain(){ inti=1,j=2;//localvariablesonthestack int*p; //apointermaybeassignedto"pointto"thevalueof //anothervariableusingthe&(addressof)operator p=&j; //sincejwasonthestack,thisaddresswillbesomewhere //onthestack.Pointersareprintedinhexformatusing //%pandconventionallymarkedwith0x. printf_s("0x%p\n",p); //The*(indirectionoperator)canbereadas"thevalue //pointedtoby". //Sincepispointingtoj,thisshouldprint"2" printf_s("0x%p%d\n",p,*p); //changingjwillchangetheresultoftheindirection //operatoronp. j=7; printf_s("0x%p%d\n",p,*p); //Thevalueofjcanalsobechangedthroughthepointer //bymakinganassignmenttothedereferencedpointer *p=10; printf_s("jis%d\n",j);//jisnow10 //allocatememoryontheheapforaninteger, //initializeto5 p=newint(5); //printthepointerandtheobjectpointedto //theaddresswillbesomewhereontheheap printf_s("0x%p%d\n",p,*p); //freethememorypointedtobyp deletep; //Atthispoint,dereferencingpwith*pwouldtrigger //aruntimeaccessviolation. //Pointerarithmeticmaybedonewithanarraydeclared //onthestackorallocatedontheheapwithnew. //Theincrementoperatortakesintoaccountthesize //oftheobjectspointedto. p=newint[5]; for(i=0;i<5;i++,p++){ *p=i*10; printf_s("0x%p%d\n",p,*p); } //Acommonexpressionseenisdereferencingincombination //withincrementordecrementoperators,asshownhere. //Theindirectionoperator*takesprecedenceoverthe //incrementoperator++. //Theseareparticularlyusefulinmanipulatingchararrays. chars1[4]="cat"; chars2[4]="dog"; char*p1=s1; char*p2=s2; //thefollowingisastringcopyoperation while(*p1++=*p2++); //s2wascopiedintos1,sonowtheyarebothequalto"dog" printf_s("%s%s",s1,s2); }
输出:
0x0012FEC8 0x0012FEC82 0x0012FEC87 jis10 0x003208505 0x003208500 0x0032085410 0x0032085820 0x0032085C30 0x0032086040 dogdog
另一个示例演示如何在数据结构中使用指针;本例中采用链接列表。
//pointer_linkedlist.cpp //compilewith:/EHsc #include<iostream> usingnamespacestd; structNewNode{ NewNode():node(0){} inti; NewNode*node; }; voidWalkList(NewNode*ptr){ if(ptr!=0){ inti=1; while(ptr->node!=0){ cout<<"node"<<i++<<"="<<ptr->i<<endl; ptr=ptr->node; } cout<<"node"<<i++<<"="<<ptr->i<<endl; } } voidAddNode(NewNode**ptr){ NewNode*walker=0; NewNode*MyNewNode=newNewNode; cout<<"enteranumber:"<<endl; cin>>MyNewNode->i; if(*ptr==0) *ptr=MyNewNode; else{ walker=*ptr; while(walker->node!=0) walker=walker->node; walker->node=MyNewNode; } } intmain(){ charans=''; NewNode*ptr=0; do{ cout<<"a(addnode)d(displaylist)q(quit)"<<endl; cin>>ans; switch(ans){ case'a': AddNode(&ptr); break; case'd': WalkList(ptr); break; } }while(ans!='q'); }
输出:
a 45 d a 789 d qa(addnode)d(displaylist)q(quit) enteranumber: a(addnode)d(displaylist)q(quit) node1=45 a(addnode)d(displaylist)q(quit) enteranumber: a(addnode)d(displaylist)q(quit) node1=45 node2=789 a(addnode)d(displaylist)q(quit)
固定和可变指针
const和volatile关键字用于更改处理指针的方式。const关键字指定指针在初始化后无法修改;此后指针将受到保护,防止进行修改。
volatile关键字指定与后跟的名称关联的值可由用户应用程序中的操作以外的操作修改。因此,volatile关键字对于声明共享内存中可由多个进程访问的对象或用于与中断服务例程通信的全局数据区域很有用。
如果某个名称被声明为volatile,则每当程序访问该名称时,编译器都会重新加载内存中的值。这将显著减少可能的优化。但是,当对象的状态可能意外更改时,这是保证可预见的程序性能的唯一方法。
若要将指针指向的对象声明为const或volatile,请使用以下形式的声明:
constchar*cpch; volatilechar*vpch;
若要将指针的值(即指针中存储的实际地址)声明为const或volatile,请使用以下形式的声明:
char*constpchc; char*volatilepchv;
C++语言会阻止将允许修改声明为const的对象或指针的赋值。此类赋值会移除用来声明对象或指针的信息,从而违反原始声明的意图。请考虑以下声明:
constcharcch='A'; charch='B';
假定前面声明了两个对象(constchar类型的cch和char类型的ch),以下声明/初始化将是有效的:
constchar*pch1=&cch; constchar*constpch4=&cch; constchar*pch5=&ch; char*pch6=&ch; char*constpch7=&ch; constchar*constpch8=&ch;
以下声明/初始化存在错误。
char*pch2=&cch;//Error char*constpch3=&cch;//Error
pch2的声明声明了一个可以用来修改常量对象的指针,因此不允许使用。pch3的声明指定pointer是常量,而不是对象;与不允许使用pch2的原因相同,也不允许使用该声明。
以下八个赋值显示了通过指针进行的赋值以及对前面的声明的指针值的更改;现在,假设pch1到pch8的初始化是正确的。
*pch1='A';//Error:objectdeclaredconst pch1=&ch;//OK:pointernotdeclaredconst *pch2='A';//OK:normalpointer pch2=&ch;//OK:normalpointer *pch3='A';//OK:objectnotdeclaredconst pch3=&ch;//Error:pointerdeclaredconst *pch4='A';//Error:objectdeclaredconst pch4=&ch;//Error:pointerdeclaredconst
声明为volatile或const和volatile的组合的指针遵循相同的规则。
指向const对象的指针通常用于函数声明中,如下所示:
errno_tstrcpy_s(char*strDestination,size_tnumberOfElements,constchar*strSource);
前面的语句声明了函数strcpy_s,其中,三个参数中的两个是指向char的类型指针。由于参数是按引用而不是按值传递的,因此,如果未将strSource声明为const,则该函数可以自由修改strDestination和strSource。将strSource声明为const可向调用方保证调用的函数无法更改strSource。
注意
由于存在从typename*到consttypename*的标准转换,因此将char*类型的参数传递到strcpy_s是合法的。但是,反之则不行;不存在从对象或指针中移除const特性的隐式转换。
给定类型的const指针可以分配给同一类型的指针。但是,非const类型的指针不能赋给const指针。以下代码显示了正确和错误的赋值:
//const_pointer.cpp int*constcpObject=0; int*pObject; intmain(){ pObject=cpObject; cpObject=pObject;//C3892 }
以下示例显示了当有指针指向某个指向对象的指针时如何将对象声明为const。
//const_pointer2.cpp structX{ X(inti):m_i(i){} intm_i; }; intmain(){ //correct constXcx(10); constX*pcx=&cx; constX**ppcx=&pcx; //alsocorrect Xconstcx2(20); Xconst*pcx2=&cx2; Xconst**ppcx2=&pcx2; }