《const指针六二维数组.ppt》由会员分享,可在线阅读,更多相关《const指针六二维数组.ppt(27页珍藏版)》请在三一办公上搜索。
1、1,五、const指针 六、二维数组,第6章 指针与数组,2,五、const指针 普通指针是没有const约束的指针,这样的指针本身既可以变动,其指向的内存单元也可更新。普通指针p进行两种运算:一种是指针的加减寻址运算,如p=&a i,p+,p+=I等,这种运算改变指针本身。另一种是访问指针运算,如*p=pj,*(p+j)=p0,这种运算读写内存,可改变间接变量而不改变指针。为了清晰界定指针的不同运算,引进const指针。指针与const组合派生出三种意义不同的形式。,3,第一种形式定义的指针称为只读指针。形式为:const type*r;const 类名*只读指针;const type*r=
2、地址表达式;const 类名*只读指针=数据区的地址;第一种形式可以不必同时进行初始化,即可以先定义r,然后再将该指针指向某个同类型的变量或数组。只读指针可以在一片只读或非只读的数据区移动,以便将数据只读地复制到其它位置。只读指针既可指向只读区也可指向变量或左值区。定义中的关键字const限定*r为右值,具体地说只读指针r是左值指针,间接变量*(r+i)或ri约束为右值表达式。,4,第二种形式定义的指针称为固定指针或右值指针。第二种形式为:type*const s=左值区的地址;类名*const 固定指针=非只读区的地址;第二种形式必须同时用变量或左值的地址进行初始化,除非作为形参。定义中的关
3、键字const冻结指针s为右值,*s或si是左值。一维数组名a就是一个这样的右值地址,r=a是可以的,a=r是错误的,这确保数组元素总是可以通过a来索引。右值指针可以用来接受new运算符申请动态自由空间成功后的结果值,以避免对堆指针加减的误操作。,5,例固定指针和只读指针(固定指针和只读指针都可指向普通(非只读)的数组)void intcpy(int*p,const int*q,int n)/只读指针形参q表示*q在函数体中为右值。for(int k=0;k/int k=0;while(kn)*p=*q;p+;q+;k+;const int c5=1,2,3,4,5;/定义只读全局数组,每一个
4、数组元素ck为右值,6,void main(void)int a10;int*const s=a;/定义固定指针s,初始化为数组名a const int*r=c;int k=0;for(;k5;k+,r+)sk=*r;intcpy(a+5,a,5);for(k=0;k10;k+)cout-ak;/输出结果:-1-2-3-4-5-1-2-3-4-5,7,只读指针本身是可变的,只读指针的间接访问不得改写它指向的存储单元。固定指针本身是不变的,但固定指针指向的存储单元允许更新。固定指针只读指针都可以指向变量,固定指针和普通指针不指向只读数据区而仅指向左值数据区。只读指针本身是可变的,只读指针的间接访
5、问不得改写它指向的存储单元。固定指针本身是不变的,但固定指针指向的存储单元允许更新。固定指针只读指针都可以指向变量,固定指针和普通指针不指向只读数据区而仅指向左值数据区。,8,例如:const int c5=1,2,3,4,5;/错误,p指向首元素,*p为左值,但c0为右值,矛盾,9,固定指针s指向只读数组首元素,*s可为左值,间接地导致c0为左值,但这与只读数组的定义矛盾;类似地普通指针p不指向只读数组。不可以对只读指针进行左值访问:const char*r=abc;/字符串安排在只读数据区,其首地址具有char*属性*r=m;/错误,*r是右值表达式左值区可作为右值区访问。从int*型转换
6、到const int*型是默许的。但是反之不然,即不将const int*型的地址隐含地转换为int*型或int*const型的指针,除非强制类型转换。,10,例指针的强制类型转换攻击只读数据区#include void swap(int*const s)int t=*s;*s=s1;s1=t;void main()constint a2=1,2;printf(%d,%d;,a0,*(a+1);int*p=(int*)(a+1);printf(%d,%d-,p-1=3,*p=4);swap(int*),11,第三种形式定义的指针可称为只读的固定指针,其格式为:const type*const
7、x=地址表达式;语句限定x,*x都为右值,因此x+,x0+是错误的。例如:const int c=0;int j=0;const int*const x=/y指向变量j,&j是int*型的地址,12,六、二维数组 1.二维数组的定义 一维数组是若干个同一类型有序变量的递增集合,由一个数组名来描述,r个一维数组需要r个数组名来标识,当r很大的时候,相应的定义语句等量增加。把r个一维数组整合在一起形成二维数组,通过一个数组名来索引数组中的每一元素。,13,二维数组的定义格式为:type d rc;类型 数组名常数1常数2;常数1指出二维数组的行数r,常数2指出二维数组的列数c。行数r和列数c是静态
8、确定的正整数。例如:typedef int type;const int r=8;int c=6;type dr sizeof c;是允许的,而 typedef int type;int r=8,c=6;type drc;是不可以的。,14,二维数组下标表达式中有二个下标如djk,第一个下标j用于索引数组的行,第二个下标k指出相对于数组某行的列。二维数组一经定义就具有如下性质:a.行数r和列数c一起确定数组的元素个数为 r*c。b.数组元素,二维数组第j+1行第k+1列的元素表示为d j k,j合理的取值范围为 0r-1,k合理的取值范围为:0c-1。d j k 等价于访问指针形式(*(*(d
9、+j)+k),两者为type型左值,其作用相当于变量。数组元素占住内存的字节数为n=sizeof(type)=sizeof(d j k)。,15,c.二维数组名具有两个信息,数组名d代表数组的首地址和大小。d+j具有type(*)c型的属性,指向第j+1行即d+j 等价于&dj。数组占住内存空间大小为:sizeof(d)=r*c*n个字节。sizeof(d+j)=sizeof(type(*)c)=sizeof(type*)。sizeof(d)不等于sizeof(d+0)。d.二维数组某行的首地址,dj给出第j+1行的首地址。可以认为二维数组的某行相当于一个一维数组,即dj的作用相当于一维数组名
10、的作用。d j 等价于*(d+j),两者为type*型的右值,d j+k或*(d+j)+k 等价于&djk。dj和d+j具有相同的地址值但地址属性不同。r=sizeof(d)/sizeof(d0),sizeof(dj)=c*sizeof(type)。而sizeof(dj+k)=sizeof(type*)。,16,e.二维数组的元素在内存空间中是按照行的次序递增排列的,先排列第一行的每一个元素,再排列第二行的每一个元素;第一行最后一个元素d0c-1紧邻第二行第一个元素d10;二维数组第一个元素为d00,二维数组最后一个元素为dr-1c-1。二维数组某行的首地址dj也是顺序连续递增的。每一行可以当
11、作一个一维数组。,17,例如:数组定义语句short b34;double d43;就定义两个二维数组,它们都有12个数组元素。但b占有 sizeof(short)*3*4=24 个字节的内存,d占有 sizeof(double)*4*3=96 个字节的内存。b,d都代表数组的首地址,d具有类型属性double(*)3,b是short(*)4 类型属性的地址。定义语句中的3和4用于界定数组的维数。b23,d32分别是这两个数组的最后一个元素,b34,d43是合法的表达式,但导致越界,其中的3和4是索引内存的下标。定义语句分配唯一的一片内存,下标表达式ai,dij等用于访问或操作内存,编译器不检
12、查下标是否越界。,18,例如:数组int x24与char z42在内存中的排放次序分别为:x00,x01,x02,x03,x10,x11,x12,x13 z00,z01,z10,z11,z20,z21,z30,z31 从上可见元素z12与z20 距离数组z的首地址具有相同的偏移,x04将索引到元素x10。而x14的索引方式导致越界。从上可见最后一个下标变化最快。,19,x i 是int*型的右值地址,z i 是char*型的右值地址。x i 和z i 是不同类型的地址。x i j 是int型的左值,z i j 是char型的左值。因此存在x i j=z j i 和 z i j=x j i 形
13、式的表达式,它们引起类型转换。不存在x i=z j 和z i=x j 形式的表达式,右值不能放在赋值符号的左边,并且不同类型的地址也不许隐含类型转换。,20,例 从二维数组中查找第一个出现的负数#include void main()const int n=3,m=2;int d n m;/定义二维数组 coutd i j;for(i=0;in;i+)for(j=0;jm;j+)goto found;cout not found if(*(*(d+I)+j)0)!endl;goto end;,21,found:coutd i j=dijendl;end:;某次运行的结果:另一次运行的结果:in
14、put 6 integers:2 3-4 5 6 7 input 6 integers:2 3 4 5 6 7a02=-4 not found!说明:在负数没有找到之前,双重循环正常运行,若循环结束时未找到负数,则显示not found!,然后执行goto end语句,转至end标号处。这里是一个空语句。由此看出,使用goto语句转移至某个没有语句的位置时,这个位置应放置一个空语句。这是空语句的一种用法。在循环中,若找到第一个出现的负数时,用goto语句退出两层循环,显示找到的负数。然后执行下面的空语句,进而结束程序的执行。,22,2.二维数组的初始化二维数组初始化定义语句的格式为:type
15、drc=initialList;类型 数组名常数1常数2=初始化列表;在初始化列表的初值个数足以确定二维数组的大小时可以省略地第一个常数的大小。即:type d c=initialList;类型 数组名 常数2=初始化列表;二维数组的初始化相当于多个一维数组的初始化的延拓,例如:int d24=1,2,3,4,5,6,7,8;,23,可省去左边第一个定界的维数,但最右边定界的维数是必须的:int d 4=1,2,3,4,5,6,7,8;也可以加上花括号:int d4=1,2,3,4,5,6,7,8;花括号的情形对于不完整的初始化格式是必须的;int d 4=1,2,5,6,7;等价于:int
16、d24;d00=1;d01=2;d02=0;d03=0;d10=5;d11=6;d12=7;d13=0;,24,无花括号的情形:int d 4=1,2,5,6,7;对应:int d 24;d00=1;d01=2;d02=5;d03=6;d10=7;d11=0;d12=0;d13=0;类似地:int d34=1,2,3;导致数组第一列的元素分别初始化为1,2,3,其余设置为0。类似地:int d34=1,2;导致数组第一列的元素分别初始化为1,2,0,其余设置为0。不可以写为:int d34=1,3;,25,3.多维数组 多维数组的定义的格式为:类型 数组名表达式1 表达式2 表达式n;type
17、 array max1 max2.maxn=初始化列表;初始化语句定义多维数组时,可以不指定第一维的维数,但其余的维数是必须给定的:type array max2.maxn=初始化列表;n维数组的下标表达式有n个下标,如下:array sub1 sub2.subn 下标运算符 是从左向右结合的,最左下标表达式array sub1 首先求值,然后求下标表达arraysub1sub2,依此类推。多维数组在内存单元中是连续的线性存放的。这称为多维空间到一维内存单元的映射。C/C+对数组元素的安排按照第一个下标优先的顺序进行.,26,对于二维数组先存放第一行,接着存放第二行。对于三维数组先存放第一页,接着存放第二页。这也意味最后一个下标变化最快。例如:对于三维数组 type smrc;/*m,r,c是预先静态给定的正数*/先存放第一页的r*c个元素s0rc;接着存放第二页的r*c个元素s1rc。si的地位相当于二维数组名d,对于特定的一页,先存放该页二维数组第一行,接着存放第二行。依此类推。对于int s224在内存中的排放次序为:s000,s001,s002,s003,s010,s011,s012,s013,s100,s101,s102,s103,s110,s111,s112,s113,27,请打开“第6章(3).ppt”,