[IT认证]指针详细介绍.ppt
void main(void)float s10=90,95,88,70,65,86,74,80,92,84;float aver=ave(s,10);cout“aver=“avern;,float ave(float a,int n)int i;float sum=a0;for(i=1;in;i+)sum=sum+ai;return sum/10;,float ave(float*a,int n)int i;float sum=a0;for(i=1;in;i+)sum=sum+ai;return sum/10;,第7章 指针,本章导读,指针是C+语言编程中最重要的概念之一,也是最容易产生困惑并导致程序出错的问题之一。,指针用于存储数据和程序的地址,这是指针的基本功能。,利用指针编程可以表示各种数据结构,进行各种奇妙有效的运算;,通过指针可使主调函数和被调函数之间共享变量或数据结构,便于实现双向数据通讯;并能像汇编语言一样处理内存地址,从而编写出精练而高效的程序。,本章提要:,1变量地址的概念,2一级及多级指针的概念,3指针的定义与引用,4一维数组与指针的关系,5二维数组与指针的关系,6字符串和指针的关系,7特殊指针常指针,8指针作为函数的参数,9引用,10动态数据的申请和释放,字节地址的概念,系统根据程序中定义变量的类型,给变量分配一定的长度空间。字符型占1个字节,整型数占4个字节.。内存区的每个字节都有编号,称之为地址。,内存单元的地址,内存单元的内容,7.2 指针,变量地址概念,设有说明语句:char ch=65;int i=8;float x=3.14;,变量的两种访问方式,1、直接访问,按变量名字存取变量的值。cini;实际上是按地址将输入直接放到定义 i 单元中。,2、间接访问,将变量的地址存放在另一个单元p中,通过 p 取出变量的地址,再针对变量操作。,指针的概念,一个变量的地址称为该变量的指针。,如果在程序中定义了一个变量或数组,那么,这个变量或数组的地址(指针)也就确定为一个常量。,变量的指针和指向变量的指针变量,变量的指针就是变量的地址,当变量定义后,其指针(地址)是一常量。,int i;,&i:2000H,可以定义一个变量专门用来存放另一变量的地址,这种变量称之为指针变量。在编译时同样分配一定字节的存储单元,未赋初值时,该存储单元内的值是随机的。,7.2.1 指针变量的定义,指针变量定义的一般形式为:,类型标识符*变量名,int*i_point;,指针类型,变量名,指针变量同样也可以赋值:,int i;int*i_point=,2000H,i,2000H,运算符*和指针变量,一个指针变量只能指向同一类型的变量。即整型指针变量只能放整型数据的地址,而不能放其它类型数据的地址。&运算符表示“地址”。,*在定义语句中只表示变量的类型是指针,没有任何其它意义。int*p=&a,*和指针变量结合表示“指向”,表示对指针变量指向的内存单元(变量)进行读/写操作。*p=3,*point=5;,表示指向,表示类型,5,int i;,int*point=,指针和指向,指针变量只能指向和其同类型的变量,不能指向其它类型的变量,也不能指向常量和表达式。C+允许将一个常量经强制类型转换后赋值给指针变量。int*p=(int*)0 x2000;/初始化指针变量p,使其值为0 x2000float*f=(float*)100;/初始化指针变量f,使其值为100,如果在定义指针变量时没有初始化,其值是随机的、不确定的(静态存储类型、文件作用域类型的除外,=0)。例:定义指针变量,使其指向另一个变量。int i,*p;/指针p的值是随机的p=/指针赋值,使其指向变量i,p指向不确定,不能用,例:通过指针变量,输入/输出数据。#includevoid main(void)int a,b,*p1,*p2;p1=/通过指针输出,间接表示a,间接表示b,7.2.2 指针变量的引用,a,b,&a,&b,p1,p2,4,8,输出:4 8 4 8,直接输出变量,通过指针引用间接输出变量,例:指针必须指向某变量后才能正确引用。void main(void)int x,y,*p1,*p2;*p1=5;*p2=10;,错误,p1和p2未指向任何变量,void main(void)int x,y,*p1,*p2;p1=,正确,例:通过指针交换变量。输入a,b两个整数,按大小输出这两个数。void main(void)int*p1,*p2,*p,a,b;cinab;/输入a、b p1=,b,p1,p2,a,&a,&b,4,8,b,p1,a,&a,&b,4,p2,8,交换前,交换后,对于指针变量,系统也为其分配地址,同样可以定义一个变量来存放其地址。int x,*p1,*p2;p1=二级指针变量的定义和赋初值方法如下:*=初值;,p2,p1,x,&p1,&x,可以缺省,7.2.3 多级指针,在+语言中,把这种指针型变量称为“指针的指针”,意为这种变量是指向指针变量的指针变量,也称多级指针。通常使用的多级指针是二级指针,相对来说,前面介绍的指针变量可以称为“一级指针”。,int*ptr=,但是:pptr=,则:*ptr相当于i*ptr=5相当于i=5*pptr也相当于i*pptr=8相当于i=8*pptr相当于ptr*pptr=&i相当于ptr=&i,非法,基类型不符,设有:,7.3.1 指针与一维数组 一级指针与一维数组的关系十分密切。指向一维数组的指针变量,实际上是指向一维数组元素的指针变量。可以利用指向一维数组的指针变量,完成对数组数据的操作处理。,int a5,*p,*p1;p=a;p1=&a0,p指向a,p1指向a0,int a10,*p;p=/正确,p指向a5,数组名为一常指针,不能赋值和修改,说明一个指针变量(注意类型要与数组元素相同)并使之指向数组的某个元素,则可以使用该指针变量来操作数组。int a10,b10;int*ptra,*ptrb;ptra=由于指针使用的灵活性,可以在一个数组定义后再用指针对数组进行处理。例:用指针访问数组.cpp,总结(对一维数组):1)数组名等同于数组第0个元素的地址,也就是数组的起始地址,相当于一个常指针。2)指针指向数组的首地址后,其值+i后指向数组的第i个元素。3)指针指向数组a的首地址后,可以用指针代替数组名使用,*(point+i)、*(a+i)、ai、pointi、*ai、*pointi等同,都是表示一维数组的第i个元素。若point指向a2,*(point+i)表示什么?,指针变量的运算主要有三种:赋值运算、算术运算和关系运算。,1.赋值运算inta,*pa;pa=相同类型指针赋值,把变量a的地址赋予指针变量pb,7.3.2 指针的运算,同类型指针赋值的含义是什么?,char*ptr;ptr=Iloveyou!;把字符串的首地址赋予指针变量char*str;cin.getline(str);/错误指针变量str未赋值,指向不确定,可以对指针变量赋0值。指针变量赋0值后称为空指针,空指针不指向任何地方。int*p=0;/p不指向任何地方 或 int*p=NULL;/p不指向任何地方,NULL相当于0指针#define NULL 0,2.指针的算术运算inta5,*pa;pa=a;/pa指向数组a,也就是指向a0 pa=pa+2;/pa指向a2,即pa的值为&pa2一般的,在指针指向一个数组后,指针加减一个整数的算术运算才有实际意义。指针变量加减一个整数n的含义是将指针指向的位置向前或向后移动n个元素。指针偏移,一般的,当有了定义“int a10,*p=a;”后,有:*(p+1)=2;/p+1为a1地址*(a+1)=2;/a+1为a1地址*+p=2;/+p为a1地址p=p+1;*p=2;/p=p+1与+p等价a1=2;/直接对数组元素赋值p1=2;/指针名作数组名用这些行是等价的,都是使a1为2。,int c=7,b=5,a=3,*p=/设&a=2000H,则有(*运算符是左结合的):(*p)+;/相当于a+。表达式为3,a=4,p不变*p+;/*p+首先*p,然后p=p+1,指针指向b,/表达式为3,p2004H,a不变+*p/相当于+(*p),就是*p=*p+1,即a=4*+p/相当于*(+p),首先:p=p+1,然后取*p。/即p先指向b,再取b的内容。表达式为5,/p2004H,(*p)+、+*p,*p+、*+p,4,a,注意进行算术运算的是指针本身还是指针指向的对象,两个指针变量之间只能做减法算术运算。只有指向同一数组的两个指针变量之间的减法才有意义。两指针相减所得之差是两个指针所指数组元素之间相差的元素个数。指针变量之间的加法和乘除法没有意义 设有:int a10,*p1,*p2;p1=则:p2-p1的值为3,就是p2、p1所指数组元素下标的差。,指向同一数组的两指针变量进行关系运算可表示它们所指数组元素之间的关系。设:int a10,*p1=则:p2p1 值为真,因为p2的值大于 p1的值。即&a5&a0 p2=p1 值为假,因为p1和p2不是 指向同一个变量(值不同)p1!=0 值为真,因为p1指向了 a0,不是空指针。,3.指针的关系运算,例:使用指针处理数组 void main(void)int a10,i;int*p;for(i=0;iai;for(p=a;p=a+9;p+)cout*pt;,p=a使p指向数组a;a+9为一常指针,指向数组a的最后一个元素,p=a+9表示指针p指向移动时,不能超出数组最后一个元素。,for(p=a;p=a+9;)cout*pt;,p+,p,每次p+,p就指向a的下一个元素,4.指针参与混合运算及优先级(补充)(1)*和/指针定义&*p1:先*运算再&运算&a 即a的地址(p1的值)*&p1:先&运算再*运算*(p1的指针)即变量p1值*&a:相当于*p1,即a*&放到一起互相抵消,(2)*和/指针定义 p1=&a0,b=*p1+/先取a0的值,赋值后,再使指针加1,b=100/此后p1不指向a0,指向a1 p1=&a0,b=*+p1/先+p1再*p1,左结合,b=200,p1指向a1,p1=&a0,b=(*p1)+/相当于a0+,此后p1仍指向a0/b=100先赋值,a0=101,+优先级低 p1=&a0,b=*(p1+)/相当于*p1+,括号不起作用,比较特殊/b=100,p1指向a1 p1=&a0,b=+*p1/相当于+(*p1),左结合/b=101,a0=101,p1指向a0 在涉及到与指针变量相关的操作时,特别要注意操作对象是指针变量本身还是指针变量所指的对象,以及指针变量变化后实际所指的对象。,例:输入n(不大于20)个整型数存入一维数组,用指针变量处理数组元素的方式将其逆序存放后输出。n从键盘输入。分析:用指针处理数组逆序,需要定义两个指针,具体的思路为:首先设置两个指针变量,分别指向数组的首地址(第0个元素,首指针)和数组的尾地址(最后一个元素,尾指针)。然后用循环处理,每次交换首、尾指针指向的两个元素,再令首指针后移一个元素;尾指针前移一个元素,为下一次交换做好准备。,开始时,pb指向a0,pe指向a9。在交换*pb和*pe后,pb+指向后一个元素,pe-指向前一个元素,再进行交换,反复进行,直到pb=pe。过程如下图所示:,pb,pe,交换*pb、*pe,10,1,2,3,4,5,6,7,8,9,7.3.3 指针与二维数组,二维数组是“数组的数组”,即二维数组是由若干个一维数组所组成的。二维数组有三种地址:数组地址、行地址和元素地址。设有定义“int a34;”,则三种地址为:数组首地址:a 行地址:a+0、a+1、a+2 元素地址:a i 或a i+j或&a i j 等,int a34;/设数组a的首地址是2000H,a,数组首地址是2000H,三个行地址分别是2000H、2010H和2020H,各数组元素地址从2000H到202CH连续分布。a0、a1、a2为组成二维数组a的3个一维数组名字(一维数组名是元素指针)。,下面分析二维数组名与其所分解成的多个一维数组名之间的关系。a=一维数组名是一个元素指针二维数组名是一个行指针,ai:表示第i行第0列元素的地址a+i:表示第i行地址(偏移i行)&ai:表示第i行地址,&不表示取地址,用来区 分元素地址和行地址.*(a+i):表示第i行第0列元素的地址,*不表示 取内容,用来区分元素地址和行地址.元素指针ai前加&转换成行指针行指针(a+i)前加*转换成元素指针 元素指针算术运算是偏移多少元素行指针算术运算是偏移多少行结合指针偏移的概念理解元素指针和行指针是不同的指针*的4种作用:乘、指针定义、取内容、区分为元素地址(运算符&),a00=*a0=*(*a);ai0=*ai=*(*(a+i);aij=*(ai+j)=*(*(a+i)+j);&aij=ai+j=*(a+i)+j;,二维数组地址和元素的表示法,使用二维数组名、指向运算符和取地址运算符,二维数组元素有着多种表示方法,例如(优先级()*):*(a+1)+2=&a12*(*(a+1)+2)=a12*(a+1)=*(a1)=*(&a10)=a10(*(a+1)1=(a1)1=b1=a11/b=a1*(a+1)1=*(a+1)1)=*(b1)=*(&b10)=b10=a20/b=a+1,行指针,例:指针访问二维数组.CPP,7.3.4 指针数组 由同类型指针所组成的数组称为指针数组。或者当某个数组被定义为指针类型,就称这样的数组为指针数组。指针数组的每个元素都是一个指针变量。定义指针数组的一般格式为:*;可以给指针数组赋初值,赋初值有多种方式,同普通的数组。定义指针变量时的数据类型可以选取任何基本数据类型,也可以是结构体类型、枚举类型或类类型。,设有:“int a,b,c,*p3=”,,数组名,指针个数,初始化地址,定义一整型数组和指针数组,通过指针数组来输出整型数组。void main(void)int a=10,20,30,40,50,60;int*p=,输出结果:10 20 30 40 50 60,void main(void)a5=1,3,5,7,9,*pa5,*p=pa,i;for(i=0;i5;i+)pai=,p=p+1;指向pa数组下一个元素,13579,p=&pai,例:指针数组.cpp,7.3.5 指向二维数组的指针二维数组名不能赋值给一级指针和二级指针 对于一个m列的二维数组anm,可以定义一个指向它的指针:“int(*p)m;”。就可以赋值:“p=a;”。这里的指针p称为“指向数组的指针”。定义指向数组的指针的一般形式为:(*)指向二维数组的指针可以用来代替二维数组名,使用指向二维数组的指针输出二维数组中的元素。void main()int a34=0,1,2,3,4,5,6,7,8,9,10,11;int(*p)4,i,j;p=a;/指针p指向二维数组a(起始地址)for(i=0;i3;i+)/使用相对于a数组起始地址的偏移量 for(j=0;j4;j+)/用指针+偏移量生成临时指针 cout*(*(p+i)+j)t;/使用行指针p+偏移量 cout*(pi+j)t;/使用列指针pi+偏移量 cout*(*(a+i)+j)t;/使用行指针a+偏移量 cout*(ai+j)n;/使用列指针ai+偏移量,7.4 指针与函数,在C+中,指针与函数的关系密切复杂。与数组名一样,函数名也是一个常指针。函数的参数可以是指针,函数的返回值也可以是指针。还可以定义指向函数的指针。指针作函数参数,形参要求是指针变量,实参要求分为以下几种情况。实参为数组名 实参为地址 实参为指针,一、实参用数组名,形参用指针变量,void main(void)int a 10;.f(a,10);.,f(int*x,int n).,实参数组,形参指针,7.4.1 指针作为函数参数,#define N 10/选择排序void sele_sort(int*p)/形参为指针,指向实参数组int i,j,k,temp;for(i=0;iN-1;i+)k=i;/设左端点元素值最小,记下其下标i for(j=i+1;jN;j+)/与最小元素ak比较if(pjpk)k=j;/记下较小元素下标j if(k!=i)/最小元素和左端点元素交换 temp=pi;pi=pk;pk=temp;,指针名作数组名用数组方式排序,void main()int aN,i;coutai;sele_sort(a);/数组名作实参调用排序函数cout排序后的数据为:n;for(i=0;iN;i+)/输出排序后的数组coutait;coutn;,void sele_sort(int*p)/形参为指针,指向实参数组int temp,*p1,*p2,*p3;for(p1=p;p1p+N-1;p1+)p3=p1;/p3指向左端点,假设这个元素最小 for(p2=p1+1;p2p+N;p2+)/p2指向的元素与最小元素比较if(*p2*p3)p3=p2;/当前元素比最小值小,记下其地址if(p3!=p1)/如果最小值不是左端点元素,就将其和左端点元素交换temp=*p1;*p1=*p3;*p3=temp;,完全指针方式排序,二、实参为地址,形参用指针变量,void main(void)int a,b;p=a;.f(.,f(int*x,int*y).,实参地址,形参指针,接收的是地址的值,例:将两个整数按从小到大的顺序输出。voidexchang(int*p1,int*p2)/指针作形参intp;p=*p1;*p1=*p2;*p2=p;/p1、p2所指向的实参变量交换数据void main()inta,b;cinab;if(ab)exchang(,实参为地址,输入数据:9 4 运行结果:4 9,if(ab)exchang(/主调,传地址voidexchang(int*p1,int*p2)/被调通过形参指针,交换实参变量,三、实参形参均为指针变量,void main(void)int a 10,*p;p=a;.f(p,10);.,f(int*x,int n).,实参指针,形参指针,实参指针变量调用前必须赋值,接收的是地址的值,例:输入10个整数,将其中最小的数与第一个数对换,把最大的数与最后一个数对换。void exchang(int*p,int n)/指针作形参int i,j=0,k=0,t;for(i=0;ipj)j=i;/j为最大值位置 if(pipk)k=i;/k为最小值位置if(k!=0)t=p0;p0=pk;pk=t;/交换最小值和p0if(j!=n-1)t=pn-1;pn-1=pj;pj=t;/交换最大值和pn-1,void main()int a10=4,3,5,7,2,6,1,9,8,0,*pa,i;pa=a;exchang(pa,10);for(i=0;i10;i+)coutait;coutn;,指针在作实参前必须赋值!,在函数调用时,实参传递给形参的是实参指针的值,为传值方式。在被调函数中对形参指针的任何修改都不会影响实参指针的值。调用后,实参指针和形参指针指向同一对象。利用指针作函数参数实现数据的双向传递不是指指针参数本身,而是指隐藏在指针后面的指针指向变量,指针参数本身依然是单向传值。,7.4.2 返回值为指针的函数,返回指针值的函数简称为指针函数。定义指针函数的一般形式为:*();例如:int*a(int x,int y);,表示返回值为指针,函数名,参数与一般函数一样,使用函数求两个变量的最大值。#includevoid main()int a,b,*pmax;int*max(int*,int*);coutab;pmax=max(,int*max(int*a,int*b)int*p;p=*a*b?a:b;return p;/返回指针p,例:设有一结构体,包含学号、姓名和年龄,用返回值为指针的函数完成结构体数据输入。分析:本程序应定义一个结构体类型student,包括三个成员。程序由2个函数组成:输入函数和主函数。student*input();/输入函数,返回指针;主函数负责定义变量、调用输入函数,输出结构体变量中的数据。,struct student/定义结构体类型 int num;char name20;int age;stu;/定义结构体变量stustudent*input()/返回值指针指向结构体变量student*ps=/返回的指针不能指向局部变量,主调函数中要能访问ps指向的变量,void main()student*p;p=input();/input返回指针coutnumnameagen;函数所返回的指针必须是指向一个在主调函数中可以访问的变量。不能是指向被调函数中的局部变量。否则,引用返回的指针就不能得到正确的结果。,7.4.3 指向函数的指针,函数虽然不是变量,但是它在内存中占有一定的空间。函数的首地址就是该函数的入口地址,它可以赋给指针变量,使得指针变量指向函数,指向函数的指针简称为函数指针。函数指针可以用如下形式说明:(*指针变量名)();例如:int(*p)(int,int);,指向函数,函数要符合条件,函数值为整形指针,函数要有两个整形参数,定义指针,引用指向函数的指针调用函数完成加法。int func(int a,int b)return a+b;void main()int(*p)(int a,int b);/该函数返回值为整型,有两个整型参数 p=func;/使指针指向函数 cout(*p)(100,200)n;/调用函数程序运行结果:300,指向函数的指针,指向函数的指针指向的是一种特定函数,引用时,该指针所指向函数的返回值类型、参数个数、参数类型以及顺序,须与定义指针时一致。int f1(int a)/整型函数f1,要求1个整型实参 int f2(int a,int b)/整型函数f2,要求2个整型实参,void func(int(*p1)(int),int(*p2)(int,int)/函数有两个形参,均为指向函数的指针 int a,b,i,j;a=(*p1)(i);/调用函数f1,i作实参 b=(*p2)(i,j);/调用函数f2,i、j作实参 void main()func(f1,f2)/将函数名f1,f2传递给func函数,函数指针应用,1.求定积分2.牛顿切线法求解方程根,7.5 const 指针C+把使用const定义的指针称为常指针(或指针常量),常指针有三种情况。1其一般格式为(最常用):const*=在这种类型的定义中const放在*左边,表示指针指向的内存的内容不可通过指针来修改!int a,b;const int*p=a;*p=5;/错误,不能通过p修改a的值 a=10;p=/正确,a可修改,p可改指常用作函数参数,p为常指针,第二种const指针,其一般格式为:*const=这种类型的指针定义中,const在*和变量名之间,表示指针变量的指向不可以改变!例如:int num=0,k;int*const p=/错误,p不能改指向其它变量,第三种const指针,其一般格式为:const*const=这种const指针是前两种的结合,使得指向的内容和地址都不能发生变化。例如:double pi=3.14159;const double*const ptr=这时,既不能通过ptr修改pi的值,也不能改变常指针ptr的指向。这种类型的指针较少使用。,使用const将大大改善程序的健壮性。另外const 的一些强大的功能在于它在函数声明中的应用。在一个函数声明中,const 可以修饰函数的返回值,或某个参数;对于类的成员函数,还可以修饰整个函数。void fun(const char*s1);/s1为常指针void fun1()const;/类成员函数fun1()为常函数,不允许在其中修改类的数据成员const int fun2();/常返回值,不能被修改,7.6 void型指针,表示该指针可以指向任意类型的数据无类型指针任何类型的指针都可以赋给它若将它赋给其他类型指针,需进行强制类型转换.,int a,*pi=,不能直接进行算术运算char pc10;void*p=pc;p+;/unknown size常用作函数的指针参数,编写通用程序,7.7 指针与字符串,字符串可以用字符数组表示。借助字符数组和字符串处理函数,可以很好的存储和处理字符串。存储和处理字符串还可以用指针,使用指针比用字符数组处理字符串更容易。,用字符指针处理字符串。char*str=“I am a student”;/取内存中字符串常量首地址 coutstrn;str=”I love china!”coutstrn;,输出:I am a student I love china!,用字符指针处理字符串。char*str=“I am a student”,*p=str;coutstrn;/整体输出strwhile(*p)cout*p+;/逐个字符输出strcoutn;for(int i=0;istrlen(str);i+)/输出str cout*(str+i);coutn;,*p代表一个字符,*(str+i)代表一个字符,Str为一串字符,*p为一个字符,0,将字符串a复制到字符串b。void main(void)char a=“I am a student”,b20;char*p1=a,*p2=b;while(*p2+=*p1+);/逐字符复制 coutatcoutbendl;,*p2+=*p1+,I,a,m,e,a,s,u,t,d,n,t,例:输入5个字符串,从中找出最大的字符串并输出。要求用二维字符数组存放这5个字符串,用指针数组元素分别指向这5个字符串,用一个二级指针变量指向这个指针数组。分析:本例处理的是字符串,需要用到字符串处理函数中的字符串输入、字符串输出、字符串比较等标准函数。程序中要含有一行:#include,void main()char a580,*p5,*q,*max;int i;for(i=0;i5;i+)/pi指向a的第i行 pi=ai;/ai是第i行的首地址for(i=0;i5;i+)cin.getline(pi,80);/输入5个字符串存入字符数组a,max=/输出最大字符,i=1,max=&a0,q=&a1,*max*qi=2,max=&a0,q=&a2,*max*qi=4,max=&a2,q=&a4,*max*q*max=a2=“Pascal”,例:将5个计算机语言名字符串按字母顺序升序排序并输出。分析:定义一个有5个元素的字符指针数组c,初始化使其指向5个字符串。再使用2个二级字符指针,指向数组c的元素。接下来使用“选择排序”法对其排序。和对数字数组排序一样。,void main(void)char*c=Forth,Basic,Java,C+,Pascal;char*temp,*p1,*p2;/二级指针p1用作外层变量,p2用作内层变量 for(p1=,输出结果:Basic C+Forth Java Pascal,排序前,排序后,以下程序判断输入的字符串是否“回文”,若是回文,输出YES。void main(void)char s81,*pi,*pj;int n;cin.getline(s);n=strlen(s);pi=s;pj=_;/pi指向串开始,pj指向最后 while(*pi=)pi+;/跳过前导空格 while(*pj=)_;while(_),pipj,s+n-1,pj-,pj-,pipj,void main(void)char t,st80=“teacher”;cint;/设输入为e fun(st,t);coutstrendl;,void fun(char*s,char c)int i=0,j=0;for(;si!=0;i+)if(si!=c)sj+=si;sj=0;return;,输出:tachr,必须以0结束,st,s,r,a,c,0,当si等于字符c时,i前进,j不动,h,t,指针形式小结,int i,*ip,f(),*fp(),(*fp1)(),*(*fp2)();int a5,*ap5,(*pa)5,*(*fap5)();,引用,引用是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。定义引用的一般格式:&=;“目标变量名”必须是已经定义过的变量或引用。符号“&”在不同的场合有不同的含义。在数据声明时(如int&a),“&”意为引用,若“&”前无类型符,则是取变量的地址(如int a,*p=&a)。,使用引用时须注意以下几点:1引用一定要在定义的时候初始化,而且初始化值不能是一个常数。int/输出为5,3引用同变量一样有地址,可以对其地址进行操作,即将其地址赋给一指针。int*p,r,/引用用new的空间这里的“*”是必须的,因为初始化引用需要的是变量名,而new返回的是指针。,5设有:int i,j,a10,/错误,不能定义引用数组int&r2=a2;/正确,定义数组元素的引用引用有两个主要用途:作为函数参数以及从函数中返回左值。,例:定义函数交换两变量,引用作函数参数。int max(int,输出结果:510,返回值是引用的函数。int a10;int/输出a5,输出:10,动态存储分配是指在运行时根据程序要求为变量等对象分配存储空间的方法。无论是基本类型变量还是派生类型变量,无论是全局变量还是局部变量,编译系统在编译时都会根据变量的类型确定其大小,这种内存的分配方式称为静态存储分配(栈)。有时只有在程序运行期间才能确定所需要的操作对象(比如动态大小的数组),这样编译系统就无法根据其大小预留存储空间,只能在程序运行时根据程序执行的要求分配内存空间,这就是动态存储分配(堆)。,7.9 动态内存分配,在程序执行时,系统会为程序预留一块专门用于供动态存储分配的存储空间,这块存储空间称为动态分配区(或自由存储空间)。程序在需要一个动态分配的数据元素时,必须向系统申请一块内存用于存放该元素,在删除这种动态分配的数据元素后必须将它所占据的存储空间释放,将该存储空间归还给动态分配区以便系统能再次分配使用。动态存储分配是由指针来实现的。在C+中,用new和delete运算符来完成申请和释放空间的任务。,new运算符用于申请动态存储空间。指针new 类型;指针new 类型(初值);指针new 类型表达式;/动态一维数组指针new 类型表达式 常量表达式;/动态二维数组int n;cinn;float*p=new floatn;,7.9.1 new运算符,使用new在堆上申请空间int*p1=new int;/p1指向new申请的空间float*p2=new float(3.14);/p2指向new申请的空间,初始化为3.14char*p3=new char20;/用p3指向在堆上申请的字符数组int(*p4)5=new int35;/用p4指向在堆上申请的3行5列的二维数组int/申请堆空间来初始化引用,例:设计一程序,实现动态内存分配。void main()int n,*p1,*p2;float(*p3)10;p1=new int10;/申请固定大小数组coutn;p2=new intn;/申请动态数组p3=new float510;/申请动态二维数组,用new运算符申请的堆空间没有名字,只能用指针指向其首地址或使用引用。在申请的空间释放以前,该指针不能再指向其它地址,以防内存泄露。当没有足够的堆空间用于分配时,new运算符返回空指针NULL。,delete运算符用于将申请的动态存储空间还给系统。delete 指针;delete 指针;/一维动态数组delete 表达式指针;/二维动态数组delete np;,7.9.2 delete运算符,delete运算符的功能是用来释放(删除)使用new在堆上申请的空间,将申请的空间归还给系统。释放new申请的堆空间。int*ptr1;ptr=new int(5);/ptr指向申请堆空间delete ptr;/释放指针ptr指向的堆空间 int/释放引用堆空间时要加“&”号,申请一维数组堆空间并释放之。int*p;p=new int10;/p指向申请数组堆空间delete p;/释放p指向的数组关于delete运算符,须注意:(1)必须使用由运算符new返回的指针;(2)该运算符也适用于空指针;(3)指针名前只用一对方括号符,并且不管所释放数组的维数。如果释放的是多维数组,方括号内数字表示该数组第1维的大小。,new、delete注意事项,用new运算符分配的内存空间的指针必须保存起来,以便用delete归还已分配的内存空间,否则会导致内存泄漏.float*fp,i;fp=new float;*fp=24.5;fp=/没有指向动态分配的空间,new、delete注意事项,delete释放一个指针(内存空间)后,不返回任何值,释放之后,指针值无意义,不能再对指针指向的内存空间赋值 float*p=new float;*p=3.2;delete p;*p=3.5;/p的指向不确定,动态内存分配应用,动态二维数组(m行n列)1)首先建立长度为m的指针数组,用二级指针指向2)为指针数组中每个指针申请长度为n的一维数组3)释放指针数组中每个指针指向的内存4)释放指针数组,指针使用常见错误,由于通过指针可以直接对内存寻址,所以指针操作具有灵活高效的特点,但如果指针使用不当,则会造成难以想象的结果。常见的有以下几种错误:定义指针变量后,未赋给它一个地址值就使用该指针变量。int*p;*p=125;定义一个整型指针,即意味着分配给该指针变量一个内存区,其中存放的值必须是另一个整型量的地址.正确的用法是:int*p;p=new int(125);同样,删除一个未赋值的指针也是危险的。int*p;delete p;,指针使用常见错误,指针悬挂问题。int*p1,*p2;p1=new int(98);p2=new int(99);p1=p2;delete p1;delete p2;,由于p1=p2使p1和p2指向同一个整型量存储区,经过上述操作后,p1原来所指的空间就无法再用,甚至无法回收,此就是所谓的指针悬挂(内存泄漏),P1,P2,98,99,指针使用常见错误,字符串的赋值。char str=C+Language;char*ps;ps=new char13;ps=str;/指针赋值delete ps;上面的第三句不起任何作用,因为第四句用字符串str的存储首地址重新为ps赋值。应该使用:strcpy(ps,str),指针使用常见错误,返回指针(或返回引用)的函数要避免函数返回后引用对象已不存在的情况。int*fun()int j=5;return/全局变量int*fun()return&j;或int*fun()static int j=5;return&j;/静态变量或int*fun()int*j=new int(5);return j;/动态分配的内存地址,7.10 链表,链表由一个头指针和一组结点组成,各个结点的类型相同,是一个结构体类型的变量。结点由两类成员构成:数据成员和链接指针成员。结点只有一个链接指针,就称链表为单向链表。有两个链接指针,就称为双向链表。链表中的结点用new从堆上申请。用一组函数处理链表,至少要有两个函数以创建和释放链表。链表可以是无序的,也可以是有序的。,头指针指向链表的第一个结点,链表的最后一个结点称为链尾结点,链尾结点的指针值为NULL,表示此链表结束。,链表的基本操作包括:1)建立链表 2)向链表中插入节点 3)从链表中查找一个节点 4)从链表中删除一个节点 5)输出链表的所有数据,1.创建链表创建过程就是“插入”一个个结点的过程。有三种插入策略,分别是插到链尾、链首和链中。插在链中可以创建有序链表。本例中采用链尾插入方案。