2019/11/11第六章指针和引用•指针(Pointer)是C++和C的一种数据类型。很多其他高级语言也有类似的数据类型。•引用(Reference)则是C++所特有的一种数据类型。•指针和引用在概念上和使用上都有相似之处,但是也有重要的差别。2019/11/1-2-第六章指针和引用6.1指针的概念6.2指针的运算6.3指针访问动态内存6.4指向结构体的指针6.5引用概念6.6指针和引用作为函数的参数6.7指针和引用作为函数的返回值6.8指针和字符串6.9通过指针访问数组2019/11/16.1指针的概念2019/11/142019/11/16.1.1指针和指针变量•指针是变量的地址。或者说是在内存中,存放某种类型变量的地址。•例如,定义了整型变量a,a的地址就是一个指针。•存放指针的变量就是指针变量。2019/11/16.1.1指针和指针变量•当声明了一个指针变量后,确定了三件事:–变量本身在内存中所分配的地址和字节数,指针变量总是占有4个字节;–系统允许的地址范围,以及地址单元内可以存放的内容;–指针变量可以进行的运算。•访问指针变量时,只能看到地址。只有通过这个地址,才能访问地址单元中的内容。这样的访问称为对于内存单元的间接访问。2019/11/1-7-6.1.2指针变量的声明和初始化•指针变量声明的格式是:类型名*变量名1,*变量名2;•例如:int*va1,*va2;char*ch1,*ch2;•指针变量在声明后,变量的值(地址)是随机的。这样的指针变量是不能安全的使用的。因为其中的随机地址完全可能不是有效的数据地址。2019/11/16.1.2指针变量的声明和初始化•在声明指针变量时的“*”,有两个含义:–声明变量va1、va2、ch1、ch2都是指针变量;–说明变量va1和va2的类型是(int*)型,即指向整型变量的指针。va1和va2所指定的地址单元中,只能存放整型数据。类似地,ch1和ch2的类型是(char*)型,它们所指定的地址单元中,只能存放字符。•指针变量都是有类型的,指针变量的类型就是它所指定的地址单元中存放的数据的类型。2019/11/1-9-6.1.2指针变量的声明和初始化•指针变量的初始化有两种方法:在声明时的初始化和声明后的初始化。•声明指针变量时就进行初始化的格式是:类型名*指针变量名=&变量名;–其中的变量名应该是已经声明或定义的同类型变量名。例如:–charch1=’Y’,ch2=’A’;–char*pch1=&ch1,*pch2=&ch2;2019/11/16.1.2指针变量的声明和初始化•也可以在声明指针变量后,用赋值的方式对它们进行初始化。例如:–inti1=’Y’,i2=’A’;–int*pi1,*pi2;–pi1=&i1;–pi2=&i2;•没有初始化指针变量是不可以使用的。编译带有这样的指针变量的程序,编译系统会给出警告,而运行时会出现错误。6.2指针的运算2019/11/1-11-6.2指针的运算•表6.1指针的运算2019/11/12019/11/16.2.1指针的赋值运算•指针的赋值运算一定是地址的赋值。用来对指针变量赋值的可以是:–同类型变量的地址;–同类型的已经初始化的指针变量;–其他同类型的指针。•此外,也可以用0或者NULL对指针变量赋值。使得变量包含的是“空指针”,即不指向任何的内存物理地址。•必须注意:不同类型的指针是不可以互相赋值的。在指针赋值时,不存在类型自动转换的机制。•例6.1观察以下指针赋值运算的结果。如果将注释去掉,结果将如何?#includeiostreamusingnamespacestd;voidmain(){intva1=100,*pva1;floatvf1='A',*pvf1,*pvf2;int*pva2=NULL;coutvalueofpva2ispva2endl;pva1=&va1;pvf1=pvf2=&vf1;coutpva1&va1endl;coutpvf1pvf2endl;//pvf1=pva1;}valueofpva2is0x000000000x0012FF7C0x0012FF7C0x0012FF740x0012FF74注释去掉会出现编译错误2019/11/1-15-6.2.2间接访问运算•间接访问运算符“*”是一种一元算符,它和指针变量连用,对指针所指向的内存地址单元进行间接访问。使用的格式是:*指针变量•如果指针变量iptr指向整型变量va,*iptr就是变量va的内容2019/11/1-16-•例6.2对变量的直接访问和间接访问:写出以下程序运行结果。#includeiostreamusingnamespacestd;voidmain(){charch1='a',*ch;intk1=100;ch=&ch1;//指针ch指向变量ch1cout*ch=*chendl;//间接访问*ch='B';coutch1=ch1endl;//直接访问ch1=k1;cout*ch=*chendl;//间接访问}运行结果:*ch=ach1=B*ch=d2019/11/1-17-•例6.3定义指向指针的指针变量。观察对这种指针变量间接访问的结果。#includeiostreamusingnamespacestd;voidmain(){intva=100,*pva,**ppva;//ppva是指向指针的指针intk1=100;pva=&va;cout*pva=*pvaendl;//间接访问结果是整型数ppva=&pva;cout*ppva=*ppvaendl;//间接访问结果是地址coutpva=pvaendl;//就是指针pva的内容}运行结果:*pva=100*ppva=0x0012FF7Cpva=0x0012FF7C2019/11/1-18-6.2.2间接访问运算2019/11/1-19-6.2.3指针的算术运算•指针可以进行的算术运算只有加法和减法。•指针可以和一个整数n做加法或者减法运算。指针p和整数n相加(相减)的含义是指向当前指向位置p的前方或后方第n个数据的地址。2019/11/1-20-•例6.3通过指针的间接访问,输出下标为偶数的数组元素的值。#includeiostreamusingnamespacestd;voidmain(){intk1[10]={11,24,37,44,58,66,79,86,93,108},*k;k=&k1[0];for(inti=0;i10;i=i+2)coutk1[i]=*(k+i);coutendl;}运行结果:K1[0]=11k1[2]=37….数组第一个元素(下标为0)的地址赋值给指针k每次循环,指针加22019/11/1-21-6.2.3指针的算术运算•指针和指针的直接加法是没有意义的,也是不允许的。•指针和指针的减法是可以进行的,其意义是求出两个指针之间可以存放几个指定类型的数据。•不允许用一个整数减一个指针。•相同类型的指针可以进行各种关系运算。比较两个指针相等还是不相等。•进行指针“大于”、“小于”的比较,只是要判定指针在内存中的相对位置。•指针和一般的整数比较是没有意义的,也是不允许的。惟一可以和指针比较的整数是0。通过指针和0的比较来判定指针本身是不是空指针。2019/11/16.2.4指针的关系运算和逻辑运算2019/11/1-23-6.2.5void类型指针•void类型的指针就是“无类型”指针。声明的方式如下:void*指针名;•void类型的指针变量中存放的也是内存的地址,但是不指定这个地址单元内的数据的类型。2019/11/16.2.5void类型指针•void类型的指针的使用:–任何其他类型的指针都可以赋值给void指针。必须注意,这样赋值后的void指针的类型仍然是void。–void类型指针不可以直接赋值给任何其他类型的指针。–无论何时,void指针都不可以通过间接引用来访问内存中的数据。–要通过void类型指针访问内存的数据,必须进行指针类型的强制转换,才可以通过指针间接引用访问内存数据。2019/11/1void类型指针•void类型指针还有一个具体的应用:显示字符指针的内容。除了字符指针外,其他指针都可以直接用cout语句来输出地址值。但是,用cout输出字符指针时,则是输出它所指向的字符串。可以将字符指针强制转换为void指针,再用cout语句输出,就可以看到地址值。如:char*pch=HelloC++;coutpchendl;cout(void*)pchendl;2019/11/1-26-•例6.4使用memcpy通用复制函数复制数组。#includeiostreamusingnamespacestd;#includestringvoidmain(){charsrc[10]=012345678;chardest[10];char*pc=(char*)memcpy(dest,src,10);coutpcendl;ints1[3]={1,2,3};intd1[3];int*pi=(int*)memcpy(d1,s1,12);cout*pi*(pi+1)*(pi+2)endl;}运行结果:012345678123复制字符数据,10个字节复制整型数据,12个字节2019/11/1-27-6.2.6指针类型转换•实际应用的指针变量都是有类型的,指针类型就是指针所指的地址空间所存放的数据的类型。•除了void指针,不同类型的指针是不可以自动类型转换的,也就不能进行赋值运算。在例6-1中的语句pvf1=pva1(整型指针赋值给float指针)就是有语法错误的语句。•除了void指针,指针类型不仅不能自动转换,也不能进行强制类型转换。•若pch是字符指针,p是double类型指针,以下的语句:pch=static_castchar*(p);•在编译时有语法错误:“不能将double*转换为char*”,也就是说指针的强制类型转换也是不允许的。•但是,如果使用C风格的类型转换:pch=(char*)p;在编译时没有语法错误,仅在执行时有问题。•所以,还是要使用static_cast来进行强制类型转换。•唯一允许的是void指针和其它指针类型之间的转换2019/11/1-28-指针类型转换#includeiostreamusingnamespacestd;voidmain(){floatf=1.0;float*fPtr=&f;//浮点指针void*fv=&f;int*iPtr=static_castint*(fv);//浮点变量地址赋给整型指针coutfendliPtr:iPtr=*iPtr=hex*iPtrendlfPtr:fPtr=*fPtrendl;}2019/11/1//例6-5利用格式控制符hex可以显示整数的机器数的程序6.3指针访问动态内存2019/11/1-30-指针访问动态内存•动态内存是在程序执行时才可以申请、使用和释放的内存。也就是存放动态数据的内存区域。存放动态数据的区域称为“堆”,动态内存也称为堆内存。•动态内存不能通过变量名来使用,而只能通过指针来使用。2019/11/1-31-2019/11/16.3.1动态内存的申请和释放•C++中通过运算符new申请动态内存,运算符delete释放动态内存。•动态内存申请运算符new的使用格式:new类型名(初值)–运算的结果:如果申请成功,返回指定类型内存的地址;如果申请失败,返回NULL指针。•动态内存使用完毕后,要用delete运算来释放。delete运算符使用格式:delete指针名;2019/11/16.3.2动态数组空间的申请和释放•申请动态一维数组时,要在n