《数组与指针ppt课件》由会员分享,可在线阅读,更多相关《数组与指针ppt课件(35页珍藏版)》请在金锄头文库上搜索。
1、数组与指针ppt课件 Still waters run deep.流静水深流静水深,人静心深人静心深 Where there is life, there is hope。有生命必有希望。有生命必有希望p栈空间栈空间p堆空间堆空间空间分类空间分类数组数组-物理空间存放的本质物理空间存放的本质l定义数组就是申请了一块定义数组就是申请了一块连续连续的内存空间。用户可操的内存空间。用户可操作的作的空间大小空间大小等于数组元素等于数组元素*每个元素所占的空间大每个元素所占的空间大小。数组元素小。数组元素连续存放连续存放在这块空间中。在这块空间中。如:如: int intarray5;占用了占用了20个
2、字节,因为每个整个字节,因为每个整型数占四个字节。如给型数占四个字节。如给intarray3赋值为赋值为3,如果这,如果这块空间的起始地址为块空间的起始地址为100,那么在内存中的情况是:,那么在内存中的情况是:当你引用变量当你引用变量intarrayidx时,系统计算它的时,系统计算它的地址地址100+idx*4,对该地址的内容进行操作。,对该地址的内容进行操作。 | | |随机值随机值随机值随机值3随机值随机值100 103104 107108 111112 115116 119参照几何坐标系参照几何坐标系数组下标超界问题数组下标超界问题lC/C+语言语言不检查不检查数组下标的数组下标的超
3、界超界。如定义数。如定义数组组 int intarray10; 合法的下标范围是合法的下标范围是0 9,但如果你引用,但如果你引用intarray10,系统不会报错。,系统不会报错。如数组如数组intarray 的起始地址是的起始地址是1000,当引用,当引用intarray10时,系统对时,系统对1040号内存进行操作。号内存进行操作。而而1040可能是另一个变量的地址可能是另一个变量的地址l解决方法解决方法(1) 由程序员自己控制。在对下标变由程序员自己控制。在对下标变量进行操作前,先检查下标的合法性。量进行操作前,先检查下标的合法性。 (2) 利用容器等利用容器等 数组的缺点数组的缺点l
4、必须知道数组的确定类型和空间大小,才能定必须知道数组的确定类型和空间大小,才能定义数组。义数组。l数组的大小受到栈大小的限制。通常栈的大小数组的大小受到栈大小的限制。通常栈的大小是是2M。指针的概念指针的概念l如在某一程序中定义了如在某一程序中定义了 int x = 2;int x = 2;l如系统给如系统给x x分配的空间是分配的空间是10001000号单元,则指向号单元,则指向x x的指针是另的指针是另一个变量一个变量p p,p p中存放的数据为中存放的数据为10001000l10001000号单元的内容有号单元的内容有两种访两种访问方式问方式:访问变量访问变量x (x (直接访问直接访问
5、) )访问变量访问变量p p指向的单元的内指向的单元的内容容( (间接访问间接访问) )1000 21000xp定义指针变量定义指针变量l定义指针变量要定义指针变量要告诉编译器告诉编译器该该变量变量中存放的是中存放的是一个一个地址地址。l指针变量的主要用途是提供指针变量的主要用途是提供间接访问间接访问,因此也,因此也需要知道指针指向的单元的需要知道指针指向的单元的数据类型数据类型l指针变量的定义指针变量的定义 类型标识符类型标识符 *指针变量;指针变量; 如:如:int int *intp;intp; double double *doublep;doublep; int *p, x, *q;
6、 int *p, x, *q;类型标识符的作用?类型标识符的作用? 指针变量的操作指针变量的操作l如何让指针如何让指针指向指向某一变量?因为我们不知道系统分配某一变量?因为我们不知道系统分配给变量的真正地址是什么。给变量的真正地址是什么。用取地址运算符用取地址运算符 “ “& &” ” 解决。如表达式解决。如表达式 “ “&x” &x” 返回的是变量返回的是变量 x x 的地址。如:的地址。如:intp = &x;intp = &x;& & 运算符后面不能跟常量或表达式。如运算符后面不能跟常量或表达式。如 &2 &2 是没有是没有意义的,意义的,&(m * n + p )&(m * n + p
7、 )。也是没有意义的。也是没有意义的l如何通过指针变量处理和改变它所指向的单元的值?如何通过指针变量处理和改变它所指向的单元的值?用地址解析运算符用地址解析运算符 “ “* *” ” 解决。如解决。如 * *intp intp 表示的表示的是是 intp intp 指向的这个单元的内容。如:指向的这个单元的内容。如:* *intp = 5 intp = 5 等价于等价于 x = 5x = 5在对在对 intp intp 使用引用运算之前,必须先对使用引用运算之前,必须先对 intp intp 赋赋值值指针实例指针实例如有:如有:int X, *intp, Y;X=3;Y=4;intp=&X;1
8、000intp10044Y10003X如执行:如执行:*intp=Y+4;1000intp10044Y10008X注意注意:不能用:不能用 intp=100;intp=100;因为我们永远不知道变量存储的因为我们永远不知道变量存储的 真实地址,而且真实地址,而且程序每次运行变量地址可能都不同。程序每次运行变量地址可能都不同。指针使用指针使用l指针变量可以指向不同的变量。如上例中指针变量可以指向不同的变量。如上例中intp指向指向x,我们可以通过对我们可以通过对intp的重新赋值改变指针的指向。如果的重新赋值改变指针的指向。如果想让想让intp指向指向y,只要执行,只要执行intp=&y就可以了
9、。这时,就可以了。这时,intp与与x无任何关系。无任何关系。l同类的指针同类的指针变量之间可相互赋值,表示二个指针指向同变量之间可相互赋值,表示二个指针指向同一内存空间。一内存空间。l空指针空指针指针没有指向任何空间指针没有指向任何空间空指针用常量空指针用常量NULLNULL表示,表示,NULLNULL的值一般赋为的值一般赋为0 0不能引用空指针指向的值不能引用空指针指向的值l野指针野指针( (没有初始化的指针没有初始化的指针) )指针变量的使用指针变量的使用设有定义设有定义 int x, y; int *p1,*p2;执行语句:执行语句:x=23;y=234;执行语句:执行语句:p1=&x
10、;p2=&y;执行语句:执行语句:*p1=34;p2=p1;1000x1004y1008p11012p2100023x1004234y1008p11012p2100023x1004234y10081000p110121004p2100034x1004234y10081000p110121000p2指针实例指针实例有以下结构有以下结构 Ap1aBp2b比较执行比较执行 p1=p2和和 *p1= *p2后的不同结果。后的不同结果。 解:解: Ap1aBp2bBp1aBp2b指针的初始化指针的初始化l指针在使用前指针在使用前必须初始化必须初始化。l和别的变量一样,定义指针不初始化是一个比较和别的变量
11、一样,定义指针不初始化是一个比较普通的错误。普通的错误。l没有初始化的指针可能指向任意地址,对这些指没有初始化的指针可能指向任意地址,对这些指针作操作可能会导致程序错误针作操作可能会导致程序错误( (野指针野指针) )。lNULLNULL是一个特殊指针值,称为空指针。它的值为是一个特殊指针值,称为空指针。它的值为0 0。它可被用来初始化一个指针,表示不指向任何。它可被用来初始化一个指针,表示不指向任何地址。地址。l思考思考:int *p; *p = 5; int *p; *p = 5; 有什么问题有什么问题?指针与数组指针与数组l在在C+C+中,指针和数组关系密切,几乎可中,指针和数组关系密切
12、,几乎可以互换使用以互换使用l数组名可以看成是数组名可以看成是常量指针常量指针,对一维数,对一维数组来说,数组名是数组的起始地址,也组来说,数组名是数组的起始地址,也就是第一个元素的地址就是第一个元素的地址l如执行了如执行了p=arrayp=array,则,则p p与与arrayarray是等价的,是等价的,对该指针可以进行任何有关数组下标的对该指针可以进行任何有关数组下标的操作操作数组实际访问方式:数组实际访问方式: C+C+语言的下标运算符语言的下标运算符 是以指针作为操作是以指针作为操作数的,数的,fibonifiboni被编译系统解释为被编译系统解释为* *(fibon+i)(fibo
13、n+i)即表示为即表示为fibonfibon所指所指( (固定不可变固定不可变) )元素向后元素向后第第i i个元素个元素。无论以下标方式或指针方式存取数。无论以下标方式或指针方式存取数组元素时,组元素时,系统都是转换为指针方法实现系统都是转换为指针方法实现。l通过指针访问数组时,下标通过指针访问数组时,下标有效范围有效范围由程序员自己检查。由程序员自己检查。数组元素的指针表示数组元素的指针表示方法方法3: for ( p=a; pa+10; +p ) cout *p ; 方法方法2: for ( i=0; i10; +i ) cout *(a+i); 方法方法1: for ( i=0; i1
14、0; +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; 指针同样可以进行指针同样可以进行”+”,”-“+”,”-“运算,运算结果运算,运算结果也是指向后一个或前一个数组元素。也是指向后一个或前一个数组元素。* *但是有但是有”+”和和”-”的表达式往往容易出错,的表达式往往容易出错,必须小心使用,如:必须小心使用,如:y=*pf
15、ib+;y=*pfib+;由于后由于后”+”+”的优先级高于的优先级高于”*”*”,所以该表达,所以该表达式等效于式等效于y=*(pfib+)y=*(pfib+),又因是后,又因是后”+”+”,所以,所以y y中取值为中取值为* *pfibpfib,而,而pfibpfib增增1 1后指向下一个元素。后指向下一个元素。这条语句在这条语句在C+C+中是常用的。千万不可误解为将中是常用的。千万不可误解为将pfibpfib所指目标增所指目标增1 1。动态内存分配动态内存分配l什么时候需要动态分配内存?什么时候需要动态分配内存?l实例:顺序对一批文件进行解析,但是实例:顺序对一批文件进行解析,但是不知道
16、文件的大小,如何建立缓冲区?不知道文件的大小,如何建立缓冲区?l参看一个人脸检测的参看一个人脸检测的Ground Truth文件文件格式格式C语言方法语言方法malloc函数原型:函数原型:void * malloc(size_t n);n是要分配的内存的大小是要分配的内存的大小(字节为单位字节为单位),返回值是分,返回值是分配内存的块的首地址配内存的块的首地址 关键代码:关键代码:int * array;array = (int *)malloc(10 * sizeof(int);注意:注意:单位,单位,内存大小不能写成数组元素的个数内存大小不能写成数组元素的个数需要注意的问题需要注意的问题
17、(1)malloc函数是一个库函数,它并不是函数是一个库函数,它并不是C语言中的关语言中的关键字:键字:需要头文件需要头文件才可以使用该函数才可以使用该函数并不是所有的平台都可以使用该函数,尤其是一些并不是所有的平台都可以使用该函数,尤其是一些单片机系统单片机系统free函数原型:函数原型:void free(void * p);p是要释放的已分配内存的块的首地址是要释放的已分配内存的块的首地址释放一块动态分配的内存:释放一块动态分配的内存:例如:例如:int *p;p = (int *)malloc(sizeof(int);free(p);动态内存申请与释放动态内存申请与释放l中由中由new
18、new 和和 deletedelete两个运算符替代两个运算符替代- - 运算符运算符newnew用于进行内存申请:用于进行内存申请: 申请动态申请动态变量变量:p = new type;p = new type; 申请动态申请动态数组数组:p = new typesize;p = new typesize; 申请动态变量并初始化申请动态变量并初始化:p = new type(p = new type(初值初值) ) - - 运算符运算符delete delete 释放释放 newnew分配的内存:分配的内存: 释放动态释放动态变量变量:delete p;delete p; 释放动态释放动态数
19、组数组:delete delete p; p;/为简单变量动态分配内存,并作初始化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;输出结果:
20、输出结果:5abcde动态分配的检查动态分配的检查lnew操作的结果是操作的结果是记录记录申请到的空间的申请到的空间的起始地址起始地址及大小及大小(隐式隐式)l当系统空间用完时当系统空间用完时,new操作操作可能失败可能失败lnew操作失败时,返回空指针操作失败时,返回空指针/NULL动态内存申请与释放动态内存申请与释放/动态分配检查动态分配检查int main()int main() int *p; int *p; p = new int; p = new int; if( p= NULL ) if( p= NULL ) /if ( NULL = p )/if ( NULL = p ) co
21、ut allocation failuren; cout allocation failuren; return 1; return 1; *p = 20; cout *p; *p = 20; cout *p; delete p; delete p; return 0; return 0; assert宏宏lassert()宏在标准头文件宏在标准头文件cassert中中 lassert()有一个参数,表示断言为真的表有一个参数,表示断言为真的表达式,预处理器产生测试该断言的代码。达式,预处理器产生测试该断言的代码。如果断言不是真,则在发出一个错误消如果断言不是真,则在发出一个错误消息后程序会终
22、止。息后程序会终止。#include #include #include /#include /包含包含assertassert宏的头文件宏的头文件int main()int main() int *p; int *p; p = new int; p = new int; assert ( p != 0 ); assert ( p != 0 ); /p/p等于等于0 0,则退出程序,则退出程序 *p=20; cout *p; *p=20; cout *p; delete p;delete p; return 0; return 0; 内存分配的进一步介绍内存分配的进一步介绍l静态分配静态分配:
23、对全局变量和静态变量,编译器:对全局变量和静态变量,编译器为它们分配空间,这些空间在整个程序运行为它们分配空间,这些空间在整个程序运行期间都存在期间都存在l自动分配自动分配:函数内的局部变量空间是分配在:函数内的局部变量空间是分配在系统的栈工作区。当函数被调用时,空间被系统的栈工作区。当函数被调用时,空间被分配;当函数执行结束后,空间被释放分配;当函数执行结束后,空间被释放l动态分配动态分配:在程序执行过程中需要新的存储:在程序执行过程中需要新的存储空间时,可用动态分配的方法向系统申请新空间时,可用动态分配的方法向系统申请新的空间,当不再使用时用显式的方法还给系的空间,当不再使用时用显式的方法
24、还给系统。这部分空间是从被称为堆的内存区域分统。这部分空间是从被称为堆的内存区域分配。配。OSProgramHeap动态分配动态分配Stack自动分配自动分配Globe variables静态分配静态分配内存泄漏内存泄漏l动态变量是通过指针间接访问的。如果该动态变量是通过指针间接访问的。如果该指针被修改指针被修改,这,这个区域就被丢失了。堆管理器认为你在继续使用它们,但个区域就被丢失了。堆管理器认为你在继续使用它们,但你不知道它们在哪里,这称为你不知道它们在哪里,这称为内存泄露内存泄露。l为了避免出现孤立的区域,应该明白地告诉堆管理器这些为了避免出现孤立的区域,应该明白地告诉堆管理器这些区域不
25、再使用。可以采用区域不再使用。可以采用deletedelete操作,它释放由操作,它释放由newnew申请申请的内存。的内存。l当释放了内存区域当释放了内存区域,堆管理器重新收回这些区域,而指针,堆管理器重新收回这些区域,而指针仍然指向堆区域,但不能再使用指针指向的这些区域。仍然指向堆区域,但不能再使用指针指向的这些区域。l要确保在程序中同一个区域释放一次。要确保在程序中同一个区域释放一次。l释放内存对一些程序不重要,但对有些程序很重要。如果释放内存对一些程序不重要,但对有些程序很重要。如果你的程序要运行很长时间,而且存在内存泄漏,这样程序你的程序要运行很长时间,而且存在内存泄漏,这样程序会耗
26、尽所有内存,直至崩溃。会耗尽所有内存,直至崩溃。动态空间分配示例动态空间分配示例l输入一批数据,计算它们的和。数据个数在设输入一批数据,计算它们的和。数据个数在设计程序时尚无法确定。计程序时尚无法确定。l存储一批数据应该用数组,但存储一批数据应该用数组,但C+语言的数组语言的数组大小必须是固定的。该问题有两个解决方案:大小必须是固定的。该问题有两个解决方案:开设一个足够大的数组,每次运行时只使用开设一个足够大的数组,每次运行时只使用一部分。缺点:浪费空间一部分。缺点:浪费空间用动态内存分配根据输入的数据量申请一个用动态内存分配根据输入的数据量申请一个动态数组动态数组#include int m
27、ain() int *p, i, n, sum=0; cout An array will be created dynamically.nn ; cout n; p = new intn; if ( p = NULL ) return 1; for ( i=0; i pi; for( i=0; in; +I ) sum += pi;cout Number of elements: n endl; cout Sum of the elements: sum endl; delete p; return 0;作业作业1.实现几个字符串操作函数,实现几个字符串操作函数,(1)计算字符串长度计算字符串长度(2)字符串倒置字符串倒置(3)比较字符串大小比较字符串大小(4)查找子串查找子串2. 二阶矩阵操作二阶矩阵操作(1)矩阵加法矩阵加法(2)矩阵减法矩阵减法(3)矩阵乘法矩阵乘法个人项目一个人项目一1.简易目标检测(通过简易目标检测(通过8位灰度图像处理)位灰度图像处理)(1)读取读取 保存保存(2)自己设定阈值,二值化自己设定阈值,二值化(3)水平水平/垂直镜像垂直镜像(4)放大缩小放大缩小(5)平滑滤波去噪声平滑滤波去噪声