单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,东南大学自动化学院飞行控制研究中心,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,数组与指针,1.,空间分类,2.,为什么用指针,3.,指针的定义及使用方式,4.,指针的访问方式,(,即变量操作的本质,),5.,数组同指针的关系,6.,数组和指针作为函数的参数和返回值,数组与指针1.空间分类,栈空间,堆空间,空间分类,栈空间空间分类,数组-物理空间存放的本质,定义数组就是申请了一块,连续,的内存空间。用户可操作的,空间大小,等于数组元素*每个元素所占的空间大小。数组元素,连续存放,在这块空间中。,如:,int intarray5;,占用了,20,个字节,因为每个整型数占四个字节。如给,intarray3,赋值为,3,,如果这块空间的起始地址为,100,,那么在内存中的情况是:,当你引用变量,intarrayidx,时,系统计算它的地址,100+idx*4,,对该地址的内容进行操作。,|,随机值,随机值,3,随机值,100 103,104 107,108 111,112 115,116 119,参照几何坐标系,数组-物理空间存放的本质定义数组就是申请了一块连续的内存空间,数组下标超界问题,C/C+,语言,不检查,数组下标的,超界,。如定义数组,int intarray10;,合法的下标范围是,0,9,,但如果你引用,intarray10,,系统不会报错。如数组,intarray,的起始地址是,1000,,当引用,intarray10,时,系统对,1040,号内存进行操作。而,1040,可能是另一个变量的地址,解决方法,(1),由程序员自己控制。在对下标变量进行操作前,先检查下标的合法性。,(2),利用容器等,数组下标超界问题C/C+语言不检查数组下标的超界。如定义数,数组的缺点,必须知道数组的确定类型和空间大小,才能定义数组。,数组的大小受到栈大小的限制。通常栈的大小是,2M,。,数组的缺点必须知道数组的确定类型和空间大小,才能定义数组。,指针的概念,如在某一程序中定义了,int x=2;,如系统给,x,分配的空间是,1000,号单元,则指向,x,的指针是另一个变量,p,,,p,中存放的数据为,1000,1000,号单元的内容有,两种访问方式,:,访问变量,x(,直接访问,),访问变量,p,指向的单元的内容,(,间接访问,),1000,2,1000,x,p,指针的概念如在某一程序中定义了1000 21000 xp,定义指针变量,定义指针变量要,告诉编译器,该,变量,中存放的是一个,地址,。,指针变量的主要用途是提供,间接访问,,因此也需要知道指针指向的单元的,数据类型,指针变量的定义,类型标识符,*,指针变量;,如:,int,*,intp;,double,*,doublep;,int*p,x,*q;,类型标识符的作用?,定义指针变量定义指针变量要告诉编译器该变量中存放的是一个地址,指针变量的操作,如何让指针,指向,某一变量?因为我们不知道系统分配给变量的真正地址是什么。,用取地址运算符“,&,”,解决。如表达式“,&x”,返回的是变量,x,的地址。如:,intp=,&,运算符后面不能跟常量或表达式。如,&2,是没有意义的,,&(m*n+p),。也是没有意义的,如何通过指针变量处理和改变它所指向的单元的值?,用地址解析运算符“,*,”解决。如*,intp,表示的是,intp,指向的这个单元的内容。如:*,intp=5,等价于,x=5,在对,intp,使用引用运算之前,必须先对,intp,赋值,指针变量的操作如何让指针指向某一变量?因为我们不知道系统分配,指针实例,如有:,int X,*intp,Y;,X=3;,Y=4;,intp=,1000,intp,1004,4,Y,1000,3,X,如执行:,*,intp=Y+4;,1000,intp,1004,4,Y,1000,8,X,注意,:不能用,intp=100;,因为我们永远不知道变量存储的真实地址,而且程序每次运行变量地址可能都不同。,指针实例如有:1000intp10044Y10003X如执行,指针使用,指针变量可以指向不同的变量。如上例中,intp,指向,x,,我们可以通过对,intp,的重新赋值改变指针的指向。如果想让,intp,指向,y,,只要执行,intp=&y,就可以了。这时,,intp,与,x,无任何关系。,同类的指针,变量之间可相互赋值,表示二个指针指向同一内存空间。,空指针,指针没有指向任何空间,空指针用常量,NULL,表示,,NULL,的值一般赋为,0,不能引用空指针指向的值,野指针,(,没有初始化的指针,),指针使用指针变量可以指向不同的变量。如上例中intp指向x,,指针变量的使用,设有定义,int x,y;,int*p1,*p2;,执行语句:,x=23;,y=234;,执行语句:,p1=,p2=,执行语句:,*,p1=34;,p2=p1;,1000,x,1004,y,1008,p1,1012,p2,1000,23,x,1004,234,y,1008,p1,1012,p2,1000,23,x,1004,234,y,1008,1000,p1,1012,1004,p2,1000,34,x,1004,234,y,1008,1000,p1,1012,1000,p2,指针变量的使用设有定义执行语句:执行语句:执行语句:1000,指针实例,有以下结构,A,p1,a,B,p2,b,比较执行,p1=p2,和*,p1=*p2,后的不同结果。,解:,A,p1,a,B,p2,b,B,p1,a,B,p2,b,指针实例有以下结构 Ap1aBp2b比较执行 p1=p2和,指针的初始化,指针在使用前,必须初始化,。,和别的变量一样,定义指针不初始化是一个比较普通的错误。,没有初始化的指针可能指向任意地址,对这些指针作操作可能会导致程序错误,(,野指针,),。,NULL,是一个特殊指针值,称为空指针。它的值为,0,。它可被用来初始化一个指针,表示不指向任何地址。,思考,:,int*p;*p=5;,有什么问题?,指针的初始化指针在使用前必须初始化。思考:int*p;,指针与数组,在,C+,中,指针和数组关系密切,几乎可以互换使用,数组名可以看成是,常量指针,,对一维数组来说,数组名是数组的起始地址,也就是第一个元素的地址,如执行了,p=array,,则,p,与,array,是等价的,对该指针可以进行任何有关数组下标的操作,指针与数组在C+中,指针和数组关系密切,几乎可以互换使用,数组实际访问方式:,C+,语言的下标运算符,是以指针作为操作数的,,fiboni,被编译系统解释为,*,(fibon+i),即表示为,fibon,所指,(,固定不可变,),元素向后,第,i,个元素,。无论以下标方式或指针方式存取数组元素时,,系统都是转换为指针方法实现,。,通过指针访问数组时,下标,有效范围,由程序员自己检查。,数组实际访问方式:通过指针访问数组时,下标有效范围由程序员自,数组元素的指针表示,方法,3,:,for (p=a;pa+10;+p),cout,*p;,方法,2,:,for (i=0;i10;+i),cout *(a+i);,方法,1:,for (i=0;i10;+i),cout ai;,方法,4,:,for (p=a,i=0;i10;+i),cout,*(p+i);,方法,5:,for (p=a,i=0;i10;+i),cout,pi;,下列程序段 有无问题?,for (i=0;i10;+i),cout,*a;,+a;,数组元素的指针表示方法3:方法2:方法1:方法4:方,指针同样可以进行”,+”,”-“,运算,运算结果也是指向后一个或前一个数组元素。,*,但是有”,+,”,和”,-,”,的表达式往往容易出错,必须小心使用,如:,y=*pfib+;,由于后”,+”,的优先级高于”*”,所以该表达式等效于,y=*(pfib+),,又因是后”,+”,,所以,y,中取值为*,pfib,,而,pfib,增,1,后指向下一个元素。这条语句在,C+,中是常用的。千万不可误解为将,pfib,所指目标增,1,。,指针同样可以进行”+”,”-“运算,运算结果也是指向后一,动态内存分配,什么时候需要动态分配内存?,实例:顺序对一批文件进行解析,但是不知道文件的大小,如何建立缓冲区?,参看一个人脸检测的,Ground Truth,文件格式,动态内存分配什么时候需要动态分配内存?,C,语言方法,malloc,函数原型:,void*malloc(size_t n);,n,是要分配的内存的大小,(,字节为单位,),,返回值是分配内存的块的首地址,关键代码:,int*array;,array=(,int*),malloc(10*,sizeof(int),);,注意:,单位,,内存大小不能写成数组元素的个数,C语言方法关键代码:,需要注意的问题,(1)malloc,函数是一个库函数,它并不是,C,语言中的关键字:,需要头文件,才可以使用该函数,并不是所有的平台都可以使用该函数,尤其是一些单片机系统,需要注意的问题,free,函数原型:,void free(void*p);,p,是要释放的已分配内存的块的首地址,释放一块动态分配的内存:,例如:,int*p;,p=(int*)malloc(sizeof(int);,free(p);,free函数原型:释放一块动态分配的内存:,动态内存申请与释放,中由,new,和,delete,两个运算符替代,-,运算符,new,用于进行内存申请:申请动态,变量,:,p=new type;,申请动态,数组,:,p=new typesize;,申请动态变量并初始化,:,p=new type(,初值,),-,运算符,delete,释放,new,分配的内存:释放动态,变量,:,delete p;,释放动态,数组,:,delete,p;,动态内存申请与释放中由new 和 delete两个运算,/,为简单变量动态分配内存,并作初始化,int main(),int*p;,p=new int(99);,/,动态分配内存,并将,99,作为初始化值赋给它,cout*p;,delete p;,return 0;,/为简单变量动态分配内存,并作初始化,动态内存申请与释放,/,动态字符串的使用,int main(),int*p;,char*q;,p=new int(5);,q=new char10;strcpy(q,abcde);,cout *p endl;cout q endl;,delete p;delete,q;,return 0;,输出结果:,5,abcde,动态内存申请与释放/动态字符串的使用输出结果:,动态分配的检查,new,操作的结果是,记录,申请到的空间的,起始地址,及大小,(,隐式,),当系统空间用完时,,,new,操作,可能失败,new,操作失败时,返回空指针,/NULL,动态分配的检查new操作的结果是记录申请到的空间的起始地址及,动态内存申请与释放,/,动态分配检查,int main(),int*p;,p=new int;,if(p=NULL),/if(NULL=p),cout allocation failuren;,return 1;,*p=20;cout *p;,delete p;,return 0;,动态内存申请与释放/动态分配检查,assert宏,assert(),宏在标准头文件,cassert,中,assert(),有一个参数,表示断言为真的表达式,预处理器产生测试该断言的代码。如果断言不是真,则在发出一个错误消息后程序会终止。,assert宏assert()宏在标准头文件cassert中,#include,#include /,包含,assert,宏的头文件,int main(),int*p;,p=new int;,assert(p!=0);