第7章间接访问—指针指针的概念指针运算与数组动态内存分配字符串再讨论指针作为函数参数和返回值指针数组与多级指针指向多维数组的指针指向函数的指针指针介绍本章将介绍C++语言的一个重要的特性:指针,为了成为一个优秀的C++语言程序员,你必须掌握指针并熟练地使用它们。指针•是内存的地址并可作为数据•是一个灵活和危险的机制•允许共享处理数据•允许内存动态分配(只要需要,而非预先定义)指针的概念指针就是把地址作为数据处理指针变量:存储地址的变量变量的指针:当一个变量存储另一个变量的地址时,那我们说它就是那个变量的指针使用指针的目的:提供间接访问指针的概念续如在某一程序中定义了intx=2;如系统给x分配的空间是1000号单元,则指向x的指针是另一个变量p,p中存放的数据为10001000号单元的内容有两种访问方式:访问变量x(直接访问)访问变量p指向的单元的内容(间接访问)100021000xp定义指针变量定义指针变量要告诉编译器该变量中存放的是一个地址。指针变量的主要用途是提供间接访问,因此也需要知道指针指向的单元的数据类型指针变量的定义类型标识符*指针变量;如:int*intp;double*doublep;int*p,x,*q;指针变量的操作如何让指针指向某一变量?因为我们不知道系统分配给变量的真正地址是什么。用地址运算符“&”解决。如表达式“&x”返回的是变量x的地址。如:intp=&x;&运算符后面不能跟常量或表达式。如&2是没有意义的,&(m*n+p)。也是没有意义的如何通过指针变量处理和改变它所指向的单元的值?用引用运算符“*”解决。如*intp表示的是intp指向的这个单元的内容。如:*intp=5等价于x=5在对intp使用引用运算之前,必须先对intp赋值指针实例如有:intX,*intp,Y;X=3;Y=4;intp=&X;1000intp10044Y10003X如执行:*intp=Y+4;1000intp10044Y10008X注意:不能用intp=100;因为我们永远不知道变量存储的真实地址,而且程序每次运行变量地址可能都不同。指针使用指针变量可以指向不同的变量。如上例中intp指向x,我们可以通过对intp的重新赋值改变指针的指向。如果想让intp指向y,只要执行intp=&y就可以了。这时,intp与x无任何关系。同类的指针变量之间可相互赋值,表示二个指针指向同一内存空间。空指针指针没有指向任何空间空指针用常量NULL表示,NULL的值一般赋为0不能引用空指针指向的值指针变量的使用设有定义intx,y;int*p1,*p2;1000x1004y1008p11012p2执行语句:x=23;y=234;100023x1004234y1008p11012p2执行语句:p1=&x;p2=&y;100023x1004234y10081000p110121004p2执行语句:*p1=34;p2=p1;100034x1004234y10081000p110121000p2指针实例有以下结构Ap1aBp2b比较执行p1=p2和*p1=*p2后的不同结果。解:Ap1aBp2bBp1aBp2b指针的初始化指针在使用前必须初始化。和别的变量一样,定义指针不初始化是一个比较普通的错误。没有初始化的指针可能指向任意地址,对这些指针作操作可能会导致程序错误。NULL是一个特殊指针值,称为空指针。它的值为0。它可被用来初始化一个指针,表示不指向任何地址。思考:int*p;*p=5;有什么问题?第7章间接访问—指针指针的概念指针运算与数组动态内存分配字符串再讨论指针作为函数参数和返回值指针数组与多级指针指向多维数组的指针指向函数的指针指针运算和数组指向数组元素的指针数组元素是一个独立的变量,因此可以有指针指向它。如:p=&a[1],p=&a[i]数组元素的地址是通过数组首地址计算的。如数组的首地址是1000,则第i个元素的地址是1000+i*每个数组元素所占的空间长度指针与数组在C++中,指针和数组关系密切,几乎可以互换使用数组名可以看成是常量指针,对一维数组来说,数组名是数组的起始地址,也就是第0个元素的地址如执行了p=array,则p与array是等价的,对该指针可以进行任何有关数组下标的操作例如:有定义inta[10],*p并且执行了p=a,那么可用下列语句访问数组a的元素for(i=0;i10;++i)coutp[i];指针运算指针+1表示数组中指针指向元素的下一元素地址;指针-1表示数组中指针指向元素的上一元素地址;合法的指针操作:p+k,p-k,p1-p2指针保存的是一个地址,地址是一个整型数,因此可以进行各种算术运算,但仅有加减运算是有意义的。指针运算与数组有密切的关系数组元素的指针表示当把数组名,如intarray,赋给了一个同类指针intp后,intarray的元素可以通过intp访问。第i个元素的地址可表示为intp+i,第i个元素的值可表示为*(intp+i)。通过指针访问数组时,下标有效范围由程序员自己检查。如输出数组a的十个元素方法3:for(p=a;pa+10;++p)cout*p;方法2:for(i=0;i10;++i)cout*(a+i);方法1:for(i=0;i10;++i)couta[i];方法4:for(p=a,i=0;i10;++i)cout*(p+i);方法5:for(p=a,i=0;i10;++i)coutp[i];下列程序段有无问题?for(i=0;i10;++i){cout*a;++a;}指针和数组的区别虽然通过指针可以访问数组,但两者本质是不同的。在定义数组时为数组的各个元素分配了全部的存储区,而在定义指针时,仅仅分配四个字节的存储区存放指针地址。只有把一个数组名付给了对应的指针后,指针才能当作数组使用如有:intarray[5],*intp;arrayintp当执行了intp=array后第7章间接访问—指针指针的概念指针运算与数组动态内存分配字符串再讨论指针作为函数参数和返回值指针数组与多级指针指向多维数组的指针指向函数的指针动态分配在C++语言中,每个程序需要用到几个变量,在写程序前就应该知道。每个数组有几个元素也必须在写程序时就决定。有时我们并不知道我们需要多大的数组元素直到程序开始运行。因此希望能在程序中根据某一个当前运行值来决定数组的大小。如设计一个打印魔阵的程序,我们希望先输入魔阵的阶数,然后根据阶数定义一个矩阵动态分配方法这些问题的解决方案就是内存的动态分配。我们定义一个指针,并让它指向一个合适的内存。如:int*scores;scores=内存的起始地址;动态内存分配与回收C++中由new和delete两个运算符替代-运算符new用于进行内存分配:申请动态变量:p=newtype;申请动态数组:p=newtype[size];申请动态变量并初始化:p=newtype(初值);-运算符delete释放new分配的内存:释放动态变量:deletep;释放动态数组:delete[]p;动态内存分配与回收//为简单变量动态分配内存,并作初始化intmain(){int*p;p=newint(99);//动态分配内存,并将99作为初始化值赋给它cout*p;deletep;return0;}动态内存分配与回收//动态字符串的使用intmain(){int*p;char*q;p=newint(5);q=newchar[10];strcpy(q,abcde);cout*pendl;coutqendl;deletep;deleteq;return0;}输出结果:5abcde动态分配的检查new操作的结果是申请到的空间的地址当系统空间用完时,new操作可能失败new操作失败时,返回空指针动态内存分配与回收//动态分配检查intmain(){int*p;p=newint;if(!p){coutallocationfailure\n;return1;}*p=20;cout*p;deletep;return0;}assert宏assert()宏在标准头文件cassert中assert()有一个参数,表示断言为真的表达式,预处理器产生测试该断言的代码。如果断言不是真,则在发出一个错误消息后程序会终止。#includeiostream#includecassert//包含assert宏的头文件usingnamespacestd;intmain(){int*p;p=newint;assert(p!=0);//p等于0,则退出程序*p=20;cout*p;deletep;return0;}内存分配的进一步介绍静态分配:对全局变量和静态变量,编译器为它们分配空间,这些空间在整个程序运行期间都存在自动分配:函数内的局部变量空间是分配在系统的栈工作区。当函数被调用时,空间被分配;当函数执行结束后,空间被释放动态分配:在程序执行过程中需要新的存储空间时,可用动态分配的方法向系统申请新的空间,当不再使用时用显式的方法还给系统。这部分空间是从被称为堆的内存区域分配。OSProgramHeap动态分配Stack自动分配Globevariables静态分配内存泄漏动态变量是通过指针间接访问的。如果该指针被修改,这个区域就被丢失了。堆管理器认为你在继续使用它们,但你不知道它们在哪里,这称为内存泄露。为了避免出现孤立的区域,应该明白地告诉堆管理器这些区域不再使用。可以采用delete操作,它释放由new申请的内存。当释放了内存区域,堆管理器重新收回这些区域,而指针仍然指向堆区域,但不能再使用指针指向的这些区域。要确保在程序中同一个区域释放一次。释放内存对一些程序不重要,但对有些程序很重要。如果你的程序要运行很长时间,而且存在内存泄漏,这样程序会耗尽所有内存,直至崩溃。动态空间分配示例输入一批数据,计算它们的和。数据个数在设计程序时尚无法确定。存储一批数据应该用数组,但C++语言的数组大小必须是固定的。该问题有两个解决方案:开设一个足够大的数组,每次运行时只使用一部分。缺点:浪费空间用动态内存分配根据输入的数据量申请一个动态数组#includeiostreamusingnamespacestd;intmain(){int*p,i,n,sum=0;coutAnarraywillbecreateddynamically.\n\n;coutInputanarraysizenfollowedbynintegers:;cinn;if(!(p=newint[n]))exit(1);for(i=0;in;++i)cinp[i];for(i=0;in;++i)sum+=p[i];delete[]p;coutNumberofelements:nendl;coutSumoftheelements:sumendl;return0;}可改为:p=newint[n];assert(p!=NULL);第7章间接访问—指针指针的概念指针运算与数组动态内存分配字符串再讨论指针作为函数参数和返回值指针数组与多级指针指向多维数组的指针指向函数的指针字符串再讨论字符串的另一种表示是定义一个指向字符的指针。然后直接将一个字符串常量或字符串变量赋给它如char*String,ss[]=“abcdef”;String=“abcde”;String=ss;String“abcde”ProgramOS数据段或代码区栈堆String=“abcde”;的执行结果•字符串常量存储在一个称为数据段的内存区域里•将存储字符串”abcde”的内存的首地址赋给指针变量String。String“abcdef\0”ProgramOS数据段栈堆St