C语言课件- 指针

上传人:ra****d 文档编号:243368360 上传时间:2024-09-21 格式:PPT 页数:83 大小:1.27MB
返回 下载 相关 举报
C语言课件- 指针_第1页
第1页 / 共83页
C语言课件- 指针_第2页
第2页 / 共83页
C语言课件- 指针_第3页
第3页 / 共83页
点击查看更多>>
资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,主要内容:,(,1,)指针变量的定义和使用;,(,2,)用指针实现函数参数的引用传递;,(,3,)用指针实现动态存储分配的方法;,(,4,)用指针和结构体管理单链表;,(,5,)指针与数组的关系。,第,8,章 指针,指针是,C,语言的一个重要概念,它是,C,语言的精华之一。特点是:,调用函数时可实现变量的引用传递。,用间接访问的方式改变数据的逻辑顺序。,它能直接对内存地址操作,使用灵活。,实现动态存储管理。,可以使程序简洁、紧凑、高效。,容易产生副作用,初学者容易出错。,int i=3;,float j=6;,double k=9;,3,变量,i,变量,j,变量,k,2200,2206,2202,2214,程序中定义的,变量,系统根据它的,类型,给它分配一定长度的,内存单元,该,内存单元的起始地址即为变量的地址,。,该,内存单元的内容就是变量的值。,变量的,地址,和变量的,值,9.1,地址和指针的概念,内存是,编址,连续,的,,基本单位是,字节,。,用户数据区,直接访问和间接访问,1.,直接访问:,用,变量名,直接从它对应的,地址,存取内容。,如,: int x=5,y=8, z=0;,z,=x+y;,5,8,0,变量,x,变量,y,变量,z,2200,2202,2204,13,直接从,x,和,y,对应的地址,2200,和,2202,取出内容,5,和,8,然后相加将结果,13,存入,变量,z,对应的地址,2204,的单元中。,2.,间接访问:,定义一个存放地址的变量,p (p,的地址,1500),将,x,的地址,2200,存放在变量,p,中。通过变量,p,取出地址,2200,再按此地址存取其中的内容,就间接的完成了对,x,的存取。,当,p,的内容改为,2202,时,通过变量,p,取出地址,2202,再按此地址,存取的就是变量,y,的内容。,p,就是指针变量,5,8,0,变量,x,变量,y,变量,z,2200,2202,2204,2200,1500,变量,p,2202,地址称为,指针,,,变量的地址,就是,变量的指针,。存放地址的变量,也就是存放指针的变量,称之为,指针变量,。,9.2,指针变量,一般形式,:,基类型名 *指针变量名,;,说明,:,基类型名,:,指针变量所指向的变量的类型名称,指针变量名,:,所定义的指针变量的名称,*,:,表示它后面的变量名是指针类型,功能,:,(1),定义该指针变量名为指向基类型的指针变量,为,该变量分配存储单元,其长度等于存储地址的字节数。,(2),基类型确定用指针变量“间接”存取数据的存储单,元个数和存储形式。该变量只能指向基类型数据。,指针变量的定义,char *p1;,int *p2;,指针变量的初始化,用,=&,变量名,来给指针变量赋初值。选项中的变量名必须,是已定义过的,其,类型,必须,与基类型一致,。表示将它对应的地,址值赋给所定义的指针变量。,可以用赋值语句给指针变量赋值,:,float y, *py;,py=,例如:,int x, *p=,/*,p,为,指向整型变量x的指针变量,*,/,int px=,/*,不正确,缺,指针,定义符*号,*/,float *pf,=&x,;,/*,pf,为,指向实型变量的指针变量,*,/,/*,初值,指向类型不匹配, x,为,整型变量,*,/,指针变量的引用,1.,与指针相关的运算符,(1),&,:,取地址运算符,取右边变量的地址,(2),*,:,指,向,运算符,(,间接访问运算符,),访问*,右边的,指针变量所指向的变量。,&a,是变量,a,的地址,*,p,是指针变量,p,指向的变量,。,说明,: “&”,和“*”都是单目运算符,它们的优先级,相同,按自右而左方向结合。,如果已定义,float a, *p=,则,*,p,是变量,a,&*p,是变量,a,的地址,p,&*p,等价于,p,而,&a,是变量,a,的地址,p,*,&a,是,p,所指向的变量,a,*,&a,等价于,a,注意:,指针变量定义,的,“,*,”,(,指针类型定义符,),和,引用指针变量,的,“,*,”,(,指向运算符,),含义不同,。,float a;,float *p= ,p=,*p=a;,*p=&a,正确,正确,正确,错误,2.,指针变量的引用,(1),引用指针变量的值,(,指针,),与引用其它类型的变量一样,直接用它的变量名,(2),引用指针变量所指向的变量时,用“,*指针变量名,”,注意:,指针变量的值,与,它所指向变量的值,之间的差别,指针变量只有,正确赋值后,才能通过它访问指向的变量。,x,int x;,p,p,=&x,;,&x,int *,p,;,*,p,*,p,=5,;,5,注意事项:,(1).,指针变量,定义,和,引用,指向变量的“,*,”含义有差别。,(2).,不能引用,没有赋值,的指针变量,不要误认为,p,定义后,变量,*,p,就已存在,必须给,p,正确赋值后,变量*,p,才存在。,(3).,p,=,是,给指针变量,p,赋值,*,p,=3;,是,给,p,指向的变量赋值,。两者含义完全不同。,(4).,给指针变量赋值必须用,同类型,的指针。,(5).,指针变量只存放地址,地址值是,无符号整数,但,不能直,接用整型量,(,或其它非地址量,),赋值给指针变量。,int *,p1,=2200,;,错误,3.,两种访问变量的方式,(1),直接访问,:,直接引用,变量名,如,:,int i;,i=5,;,(2),间接访问,:,通过,指针变量,和,指向运算符,来引用,如:,int i, *,p,;,p,=,*,p,=5;,p,p,p,i,i,i,&i,&i,5,*,p,*,p,main( ), int m, n;,int *p=,printf(“input m,n:);,scanf(%d %d,p,printf(m=%d ,printf(*p=%d p=%Xn,*p,p);,printf(n=%d ,printf(*q=%d q=%Xn,*q,q);,例,9.1,取地址运算符,&,和指向运算符,*,的应用,input m,n:,123 456,m=123 &m=FFD6,*,p=123 p=FFD6,n=456 &n=FFD8,*,q=456 q= FFD8,printf(“input m,n:);,运行结果,:,scanf(%d %d,p,printf(m=%d ,printf(*p=%d p=%Xn,*p,p);,printf(n=%d ,printf(*q=%d q=%Xn,*q,q);,实现引用传递,函数调用时,实参传递给形参采用,单向的值传递,,直接用,变量名作实参,,函数对形参的,修改结果不会,带到主调函数。,若希望将多个变量的修改结果返回给主调函数,实现变量的“,引用传递,”,怎么办,?,可将,指针变量,作形参,,,变量的地址,作实参,,,就可以达到,变量地址,(,指针,),的值传递,,即,指向变量的引用传递,。,例,9.2,用,swap,函数实现两个整数的交换,#include ,void swap(,int x, int y,), int z;,z=x; x=y; y=z;,printf(nx=%d,y=%d,x ,y);,main( ), int a= 10,b=20;,swap(,a,b,);,printf(na=%d,b=%dn,a,b);,程序输出结果:,x=20,y=10,a=10,b=20,main,swap,20,b,10,a,z,10,y,20,x,10,10,20,交换失败,int x,y;,int *pt1;*pt2;,x=8;y=6;,pt1=,pt2=,if (xy) swap(pt1,pt2);,printf(x=%d, y=%dn,x,y);,swap(int *p1,int *p2),p=*p1;,*,p1=*p2;,*,p2=p;,int p;,p,8,pt1,pt2,x,y,main(),&x,&y,p1,p2,&x,&y,8,6,6,8,运行结果,:,x=6, y=8,int x,y;,int *pt1;*pt2;,x=8;y=6;,pt1=,pt2=,if (xy) swap(pt1,pt2);,swap(int *p1,int *p2),p=*p1;,*,p1=*p2;,*,p2=p;,int p;,printf(x=%d, y=%dn,x,y);,9.3,指针与数组,C,语言中规定,数组名是指针类型的符号常量,该符号常量值,等于数组首元素的地址,(,简称数,组首地址,),它的,类型是指向数组元素的指针类型,。,即,数组名是指向该数组首元素的指针常量,。,指向数组元素的指针,1.,定义指向数组元素的指针变量,定义指向数组元素的指针变量与定义指向变量的指针变量的,方法相同,.,如,:,由于数组名是指向,0,号元素的指针类型符号常量,所以,a,与,&a0,相等,。,p=,p=a;,两句等价,p=a,不是把,a,的各元素赋给,p,注意数组名与指针变量的区别,int a10;,int *,p,;,p,=,/*,p,指向数组,a,的第,0,个元素,*,/,p,=,/*,p,指向数组,a,的第,4,个元素,*,/,2.,指针运算,(1),指针变量可以进行,指向运算,和,赋值运算,(2),指针变量可以,加,(,减,),一个,整数,,指针变量每,加,(,减,)1,地址值,增,(,减,),量,(,记为,d,字节,),等于所,指向的变量类型,(,基类型,),的字节数,sizeof(type),即,d=sizeof(type),。,当,指针变量指向数组元素,时,指针变量每,加,(,减,)1,表示指针,前,(,后,),移动,1,个元素,地址值,增,(,减,)d,字节,。,如,:,int a10, *,p,;,/*,由于,sizeof(int)=2,所以,d=2,*/,p,=,/*,p,指向数组,a,的第,5,个元素,*,/,p,+;,/*,p,加,1,后,,p,指向,a6,,地址字节数增,2,*/,p,-= 3;,/*,p,减,3,后,,p,指向,a3,,地址字节数减,6,*/,int a10,*,p,;,p,=,(3),两个,同类型,的指针变量可以,相减,,得到一个,整数,,等于两者,指向变量的地址字节值之差,除以,基类型字节数,。两个指针变量之间不能进行加法、乘法、除法等算术运算。,(4),两个,同类型,的指针变量可以进行比较运算,即进行,=,=,!=,运算,比较是用它们,指向的地址值,进行比较。,(5),C,语言设置了一个指针常量“,NULL”,,称为空指针,空指针不指向任何存储单元。空指针可以,赋值给,任何类型的指针变量,,空指针也可以和,任何类型的指针变量,做,等于,或,不等于,的比较。,int a10, *p=,printf(“q-p=%d”,q-p,);,int a10, *p=,printf(“The result is: %d”,qp,);,int a10, *p=,if(p != NULL) ,注意:,指针变量的运算顺序。,+*,p,*,p,+,*(+,p,),(*,p,)+,四者之间的差别:,+*,p,先给,p,指向的变量加,1,然后取其值,(*,p,)+,先取,p,指向的变量值,然后该变量值加,1,*,p,+,取,p,所指向变量的值,然后,p,增,1,*,+,p,p,先增,1,然后取,p,所指向变量的值,main(),int,a,4=1,2,3,4,b,c,d,e,;,int *,p,=,a,;,b,=*,p,+;,printf(“n%d,%d,”,b,*,p,);,c,=*+,p,;,d,=+*,p,;,printf(“%d,%d”,c,d,);,运行结果,:,1,2,3,4,p=&a0,*,(p+),:取,p,指向变量的值赋给,b,,再,p+,则,b=a0=1,p=&a1,b=1, *p=a1=2;,*,(+p),:,p,先增,1,,然后取,p,指向变量的值,则,p=&a2,c=a2=3,+(*p),:,p,指向变量的值加,1,,再取其值,则,+a2,d=a2=4,3.,通过指针引用数组元素,在,int a10,*p=a;,定义的情况下,:,(1),p+i,或,a+i,就是,ai,的地址,。都要进行,a+id,的运算。,(2),*(p+i),或*,(a+i),就是,p+i,或,a+i,所指向的数组元素,ai,。,数组元素中的 “, ”,是变址运算符,相当于*,( + ) ,ai,相当于*,(a+i),。,(3),指向数组元素的指针变量也可带下标,如,pi,与*,(p+i),等价。,所以,ai,*(a+i) ,pi,*(p+i),四种表示法全部等价,。,(4),注意,p,与,a,的差别,,p,是变量,,a,是符号常量,不能给,a,赋值,语句,a=p; a+;,都是错的。,p &a0,a,9,a1,a2,ai,a0,p+1,a+1,p+2,a+2,p+i,a+i,p+,9,a+,9,a,引用数组元素可用,:,(,1),下标法,如,ai,pi,。,(2),指针法,如*,(p+i),或,*,(a+i),其中,p,是,指向数组,a,的元素的指针变量。,C,语言对地址运算,不做越界检查,,程序员自己控制边界,(,易出错,),main( ), int a10;,int *p,i;,for(p=a;p(a+10);p+),scanf(%d, p);,printf(n);,for(i=0;i10;i+),printf(%d, ai);,for(i=0;i10;i+),printf(%d, *(a+i);,p=a;,/*,不能省略*,/,for(i=0;i10;i+,p+),printf(%d, *p);,for(p=a;p(a+10);p+),printf(%d,*p);,例,.,输入/输出数组全部元素,(,下标法和指针法,),4.,数组或指针变量作函数参数,函数调用时,,实参,用,数组的首地址,,,形参,定义为,数组,,对应的形参和实参实际上代表,同一个数组,,实现整个数组的,引用传递,。,例,通过调用一个函数,将整型数组的所有元素都加,3,void add(int b , int n), int i;,for(i=0;in;i+) bi+=3;,main(), int i, a8=1,2,3,4,5,6,7,8;,add(a,8);,for(i=0;i8;i+) printf(“%4d”,ai);,运行结果:,4 5 6 7 8 9 10 11,a,为实参数组名,,b,为形参数组名。函数调用时,系统,并未,为,b,分配,整个数组的存储空间,(,故,b,称为虚数组,),,而是为,b,分配一个,存储地址的单元,相当于一个指针变量,用来接收,实参数组的首地址,。即,b,指向了,a0(b=&a0),函数中,对,bi,的访问就是对,ai,的访问,所有,bi,加,3,,相当于所有,ai,加,3,,从而实现了整个数组的,引用传递,。,可以将实参和形参的,数组名,改成对应的,指针变量,效果完全等价(四种形式),void add(int *b, int n), int i;,for( i=0; in; i+) *(b+i) += 3;,main(), int a8=1,2,3,4,5,6,7,8,*p=a;,add(p,8);,for(p=a;pa+8;p+),printf(“%4d”,*p);,归纳起来,如果有一个数组,想在被调用的函数中改变,其元素的值,实参与形参的对应关系有以下四种,:,(1).,实参和形参都用数组名。,(2).,实参用数组名,形参用指针变量。,(3).,实参用指针变量,形参用数组名。,(4).,实参和形参都用指针变量。,实质都是,地址值的传递。,字符指针、字符数组和字符串,1.,用,字符指针,访问,字符数组,和,字符串,例,用,字符型数组名,和,字符指针变量,两种方法整体输入,/,输出字符串。,main(),char,s,81=Hello!, *,p,=,s,;,char *,ps,=“Welcome to you!”;,printf(“%sn”,s,);,printf(“%sn”,ps,);,gets(,s,);,printf(%sn,s,);,gets(,p,);,printf(%sn,s,);,/*,定义,ps,为指向字符串首地址的指针变量*,/,/*,用字符型数组整体输出字符串*,/,/*,用字符指针整体输出字符串*,/,/*,用字符型数组整体输入带空格的字符串*,/,/*,用字符型指针整体输入带空格的字符串*,/,Hello!,Welcome to you,!,How do you do? ,How do you do?,How are you?,How are you?,注意:字符指针变量,和,字符数组,的区别,(1),存储的内容不同,:,字符数组,存储着,字符串的内容,而,字符指针变量,存放的是,字符串首元素的地址,不是它的内容。,(2),分配的内存单元不同,:,字符数组,分配一段有确定地址的内存,。而,指针变量,只,分配,用来存放地址,的内存单元,该指针变量可以指向一个字符型数据,但若未赋初值,则它并未指向一个明确的地址。此时它指向的变量并不存在。,(3),赋值方法不同,:,对,字符数组,只能在,定义时整体赋初值,不能引用时进行,整体赋值。赋值语句只能对各个元素分开赋值。,如,: char,s,16; (,错误,),s,=I am a student.;,char s16=I am a student.;,(,正确,),对字符指针变量,可以采用下面方法赋值,:,char *,p,;,p,=I am a student.;,(4),指针变量的值是可以改变的,字符数组名是地址常量,其值是不能改变的。,char *,p,=“Welcome to you ”;,p,=,p,+8;,(,正确,),char,a,=“Welcome to you ”;,a,=,a,+8;,(,错误,),2.,字符串处理函数的实现,(1),字符串复制函数,strcpy,主要功能实现,方法一:,数组下标法,void strcpy(,char,s1, char,s2,), int i=0;,while(,s1,i=,s2,i) i+;,方法二:,指针法,void strcpy(,char *,s1, char *,s2,),while(*,s1,+=*,s2,+),;,空语句,(2),字符串比较函数,strcmp,主要功能实现,(,指针法,),int strcmp(,char *,s1,char *,s2,),while(*,s1,=*,s2,), if(*,s1,=0) return 0;,s1,+;,s2,+;,return (*,s1,-*,s2,);,main(), char,a,20,b,20;,gets(,a,);,gets(,b,);,printf(“a=%s, b=%sn cmp(a, b)=%d”,a,b,strcmp(,a,b,);,printf(“n strcmp(b,a)=%d”,strcmp(,b,a,);,printf(“n strcmp(b,b)=%d”,strcmp(,b,b,);,abcdefg,abc xyz,a=abcdefg,b=abc xyz,cmp(a,b)=68,strcmp(b,a)=-68,strcmp(b,b)=0,gets(a);,gets(b);,printf(“a=%s, b=%sn cmp(a, b)=%d”,a,b,strcmp(a,b);,printf(“n strcmp(b,a)=%d”,strcmp(b,a);,printf(“n strcmp(b,b)=%d”,strcmp(b,b);,运行结果:,地址越界问题,使用指针时,,系统不做越界检查,,注意以下几点:,(1),用指针变量访问数组元素,随时检查指针变化范围,,始终不能越界,。,(2),引用指针前,一定要对它,正确赋值,。不能引用没有赋值的指针变量。,(3),指针运算中注意各运算符的优先级和结合性,,多使用括号,,使程序易于理解。,(4),字符串整体输入时,一定要,限制输入的字符串长度,。,例,地址越界实例。,main(), char ps =“Hello !”;,char pt =“He is a worker.”;,char,*p=ps;,printf(“%sn”,ps);,for(p=ps;p,90123,指针数组,如果每个,数组元素,均为,指针类型的变量,,即,数组元,素的类型是指针类型,,则称这样的数组为,指针数组,。,一维指针数组的定义形式为:,基类型名 *数组名,数组长度,=,地址初值列表,;,例如:,int i,j,k,m,n;,int *,q,5=,比,*,优先级高,因此,数组名先与,结合,,,表明是数组,,,然后再与前面的*结合,表明,数组的元素是指针,,再与,基类型结合,表明元素类型是,指向基类型的指针变量,。,1.,指针数组的概念,为什么要用到指针数组呢?它比较适合于用来指向若干个字符串,使字符串处理更加方便灵活。,例如,图书馆有若干本书,想把书名放在一个数组中,(,图,a),,然后要对这些书目进行,排序,和,查询,。按一般方法,字符串本身就是一个字符数组。因此要设计一个二维的字符数组才能存放多个字符串。但在定义二维数组时,需要指定列数,也就是说二维数组中每一行中包含的元素个数,(,即列数,),相等。而实际上各字符串,(,书名,),长度一般是不相等的。如按最长的字符串来定义列数,则会浪费许多内存单元(见图,b,)。,可以分别定义一些字符串,然后用指针数组中的元素分别指向各字符串,(,见图,c),。如果想对字符串排序,不必改动字符串的位置,,只需改动指针数组中各元素的指向,(,即改变各元素的值,这些值是,各字符串的首地址,),。这样,各字符串的长度可以不同,而且移动指针变量的值,(,地址,),要比移动字符串所花的时间少得多。,例,将若干字符串按字母顺序,(,由小到大,),输出。,main(), void Sort(char *name ,,,int n);,void print(char *name ,,,int n);,char *name =Follow me,,,BASIC,,,Great Wall,,,FORTRAN,,,Computer Design;,int n=5;,Sort(name,,,n);,print(name,,,n);,void Sort(char *name ,,,int n),chartemp;,int i,,,j,,,k;,for(i=0;in-1;i+), k=i;,for(j=i+1;j0),k=j;,if(k!=i),temp=namei; namei=namek; namek=temp;,void print(char *name ,,,int n), int i;,for(i=0;in;i+),printf(%sn,,,namei);,运行结果为:,BASIC,Computer Design,FORTRAN,Follow me,Great Wall,在,main,函数中定义指针数组,name,。它有,5,个元素,其初值分别是“,Follow me”,、“,BASIC”,、“,Great,all”,、“,FORTRAN”,,“,Computer Design”,的首地址,(,见图,c),。这些字符串是不等长的,(,并不是按同一长度定义的,),。,Sort,函数的作用是对字符串排序。,Sort,函数的形参,name,也是指针数组名,接受实参传过来的,name,数组的首地址,,因此,形参,name,数组和实参,name,数组指的是同一数组,。用选择法对字符串排序。,strcmp,是字符串比较函数,,namek,和,namej,是第,k,个和第,j,个字符串的起始地址,。,strcmp(namek,,,namej),的值为:如果,namek,所指的字符串大于,namej,所指的字符串,则此函数值为正值,;,若相等,则函数值为,0;,若小于,则函数值为负值。,if,语句的作用是将两个串中“小”的那个串的序号,(i,或,j,之一,),保留在变量,k,中。当执行完内循环,for,语句后,从第,i,个串到第,n,个串这么多字符串中,,第,k,个串最“小”,。若,ki,就表示最小的串不是第,i,串。故将,namei,和,namek,对换,也就是,将指向第,i,个串的数组元素,(,是指针型元素,),与指向第,k,个串的数组元素对换。,执行完,Sort,函数后指针数组的情况,:,(,1,)指向指针的指针,(,二级指针变量,),在掌握了指针数组的概念的基础上,下面介绍指向指针数据的指针变量,简称为,指向指针的指针,。前面的例子中,,name,是一个指针数组,它的每一个元素是一个指针型数据,其值为地址。,name,是一个数组,它的每一元素都有相应的地址。数组名,name,代表该指针数组的首地址。,name+i,是,namei,的地址。,name+i,就是,指向指针型数据的指针,(,地址,),。于是,可以设置一个指针变量,p,,让它指向指针数组的元素,那么,,p,就是指向指针型数据的指针变量,,也就是,二级指针变量,。,怎样定义一个指向指针数据的指针变量呢?如下:,char *p;,2.,用,二级指针变量,访问指针数组,(,2,)用,二级指针变量,访问指针数组,例如:,int a=3,*q=,int *p,;,/*,二级指针*,/,p=,printf(“%d”,*p,);,若要通过该,二级指针变量,来访问,指针数组元素,所,指向的变量的内,容,,则需要进行,两次间接访问,。,用所定义的,二级指针变量,来指向,指针数组,的,各元素,,进行,间接访问,。,&a,8,&q,a,q,p,4800,2600,1700,例,指针数组的各元素指向整型数据的简单实例,main(),static,int s5=10,20,30,40,50;,int *,q,5=,int,*,p,;,for(,p,=,q,;,p,q,+5,;,p,+,),printf(%dt,*,p,);,运行结果:,10 20 30 40 50,数组名,q,是指向数组首元素的地址,而数组首元素是一个指针变量,所以,q,是指向一个指针变量的指针,是一个,二级指针常量,。,数组名,是指向该数组首元素的指针常量,数组名,等于数组首元素的地址,指针数组,是每个,数组元素都是,指针,的数组,二级指针变量,是,指向,指针,的指针变量,指针数组名,是,指向数组首元素的指针常量,,因为其数组元素本身又是,指针类型,,所以,指针数组名,是,指向指针的指针常量,,称之为,二级指针常量,。,可以将,二级指针常量,赋值给,同类型的,二级指针变量,,所以可以将,指针数组名,赋值给,同类型的,二级指针变量,。,int *q5;,int *p;,多维数组和指向分数组的指针,1.,多维数组的地址,以二维数组为例,设二维数组,a,有,3,行,4,列。,int a34=1,3,5,7,9,11,13,15,17,19,21,23,a,是数组名,a,数组有,3,行,即,3,个分数组,:,a0,a1,a2,。每个分数组又是含,4,个列元素的一维数组。,用指针变量可以指向一维数组,也可以指向多维数组。但在概念上和使用上,多维数组的指针比一维数组的指针要复杂一些。,从宏观角度去看二维数组,可以把二维数组的每一行看成是它的一个元素,每个元素都是一个一维数组,或者说每个元素都是一个分数组。,基于如此宏观的理解,数组名,a,是指向数组首元素的指针,而二维数组的,第,0,个元素,就是,第,0,行,,也就是,第,0,个分数组,。所以,a,是指向,第,0,个分数组,的指针。,a+1,则指向,第,1,个元素,也就是,第,1,行,或者说指向第一个分数组,。如果二维数组的首地址为,2000,,则,a+1,为,2008,,因为第,0,行有,4,个整型数据,因此,a+1,的含义是,a1,的地址,即,a+42=2008,。,a+2,代表第,2,行的首地址,它的值是,2016,,见图。,a0,、,a1,、,a2,既然是一维数组名,而,C,语言又规定了,数组名代表数组的首地址,,因此,a0,代表第,0,行一维数组中第,0,列元素的地址,即,&a00,。,a1,的值是,&a10,,,a2,的值是,&a20,。,a,1,3,5,7,9,11,13,15,17,19,21,23,a+1,a+2,a0,a0+2,a1,a1+2,a2,a2+2,a0+3,a0+1,a,等价,&a0,*a,等价,a0,a1,等价,&a10,,,*,a1,等价,a10,a1+2,等价,&a12,,,*,(a1+2),等价,a12,*,(,*(a+1),+2),等价,*,(,a1,+2),等价,a12,a+1,等价,&a1,*(a+1),等价,a1,请考虑,第,0,行第,1,列元素的地址,怎么表示?可以用,a0+1,来表示,见图。此时“,a0+1”,中的,1,代表,1,个列元素的字节数,即,2,个字节。今,a0,的值是,2000,,,a0+1,的值是,2002(,而不是,2008),。这是因为,现在是在一维数组范围内讨论问题,的,正如有一个一维数组,s,,,s+1,是其第,1,个元素地址一样。,a0+0,、,a0+1,、,a0+2,、,a0+3,分别是,a00,、,a01,、,a02,、,a03,的地址,(,即,&a00,、,&a01,、,&a02,、,&a03),。前面讲到,,a0,和*,(a+0),等价,,,a1,和*,(a+1),等价,,ai,和*,(a+i),等价,。因此,,a0+1,和*,(a+0)+1,的值都是,&a01 (,即图中的,2002),。,a1+2,和*,(a+1)+2,的值都是,&a12(,即图中的,2012),。请注意不要将*,(a+1)+2,错写成*,(a+1+2),,后者变成*,(a+3),了,相当于,a3,。,进一步分析,欲得到,a01,的值,用地址法怎么表示呢?既然,a0+1,和*,(a+0)+1,,是,a01,的地址,那么,*,(a0+1),就是,a01,的值。同理,*,(*(a+0)+1),或*,(*a+1),也是,a01,的值。*,(ai+j),或*,(*(a+i)+j),是,aij,的值。请记住,*,(a+i),和,ai,是等价的,。,有必要对,ai,的性质作进一步说明。,ai,从形式上看是,a,数组中第,i,个元素。如果,a,是一维数组名,则,ai,代表,a,数组第,i,个元素所占的内存单元。,ai,是有物理地址的,是占内存单元的。但如果,a,是二维数组,则,ai,是代表一维数组名。,ai,本身并不占内存单元,它也不存放,a,数组中各个元素的值。,它只是一个地址,(,如同一个一维数组名,s,并不占内存单元而只代表地址一样,),。,a,、,a+i,、,ai,、*,(a+i),、*,(a+i)+j,、,ai+j,都是地址。*,(ai+j),、*,(*(a+i)+j),是二维数组元素,aij,的值。有些同学可能不理解为什么,a+1,和*,(a+1),都是,2008,呢?他们想“,a+1,的值和,a+1,的地址怎么都是一样的呢”?的确,二维数组中有些概念比较复杂难懂,要反复思考。首先说明,,a+1,是地址,(,指向第,1,行首地址,),,而*,(a+1),并不是“,a+1,单元的内容,(,值,)”,,因为,a+1,并不是一个实际变量,也就谈不上它的内容。*,(a+1),就是,a1,,而,a1,是一维数组名,所以也是地址。以上各种形式都是地址计算的不同表示。,为了说明这个容易搞混的问题,举一个日常生活中的例子来说明。有一个排,下设,3,个班,每班有,10,名战士。规定排长只管理到班,班长管理战士。在排长眼里只有第,0,、,1,、,2,班,(,为与,C,语言中数组下标一致,假定班号也从,0,开始,),。排长从第,0,班的起始位置走到第,1,班的起始位置,看来只走了一步,但实际上它跳过了,10,个战士。这相当于,a+1(,见图,),。为了找到某一班内某一个战士,必须给两个参数,即第,i,班第,j,个战士,先找到第,i,班,然后由该班班长在本班范围内找第,j,个战士。这个战士的位置就是,ai+j(,这是一个地址,),。开始时班长面对第,0,个战士。注意,排长和班长的初始位置是相同的,(,如前面图中的,a,和,a0,都是,2000),。但它们的“指向”是不同的。,排长“指向”班,他走一步就跳过,1,个班,而班长“指向”战士,走一步只是指向下一个战士。可以看到排长是“宏观管理”,只管班,在上图中是控制纵向,班长则是“微观管理”,管理到战士,在图上是控制横向。如果要找第,1,班第,2,个战士,则先由排长找到第,1,班的班长,然后,由班长在本班范围内找到第,2,个战士。二维数组,a,相当于排长,每一行,(,即一维数组,a0,、,a1,、,a2),相当于班长,每一行中的元素,(,如,a12),相当于战士。,a+1,与,a0+1,是不同的,,a+1,是第,1,行的首地址,,a+1,指向第,1,行,(,相当于排长走到第,1,班的开头,),,而*,(a+1),或,a1,或,a1+0,都指向第,1,行第,0,列元素,(,相当于第,1,班第,0,个战士,),,二者地址虽相同,但含义不同了。前者是“纵向控制”,后者是“横向控制”。,a,a0,的值虽然相同,(,等于,2000),,但是由于指针的类型不同,(a,是指向一维数组,,a0,指向,a00,元素,),因此,对这些指针进行加,1,的运算,得到的结果是不同的。,请记住,二维数组名,(,如,a),是指向行的。因此,a+1,中的“,1”,代表一行中全部元素所占的字节数,(,前面图中表示为,8,个字节,),。一维数组名,(,如,a0,,,a1),是指向列元素的。,a0+1,中的“,1”,代表一个元素所占的字节数,(,前面图中表示为,2,个字节,),。,在行指针前面加一个*,就转换为列指针,。例如,,a,和,a+1,是行指针,在它们前面加一个*就是*,a,和*,(a+1),,它们就成为列指针,分别指向,a,数组,0,行,0,列的元素和,1,行,0,列的元素。反之,在列指针前面加,&,,就成为行指针。例如,a0,是指向,0,行,0,列元素的列指针,在它前面加一个,&,,得,&a0,,由于,a0,与*,(a+0),等价,因此,&a0,与,&*a,等价,也就是与,a,等价,它指向二维数组的,0,行。,不要把,&ai,简单地理解为,ai,单元的物理地址,因为并不存在,ai,这样一个变量。它只是一种地址的计算方法,能得到第,i,行的首地址, &ai,和,ai,的值是一样的,但它们的含义是不同的。,&ai,或,a+i,指向行,而,ai,或*,(a+i),指向列。当列下标,j,为,0,时,&ai,和,ai(,即,ai+j),值相等,即它们具有同一地址值。,(a+i),只是,ai,的另一种表示形式,不要简单地认为是“,a+i,所指单元中的内容”。在一维数组中,a+i,所指向的是一个数组元素的存储单元,它有具体值,上述说法是正确的。而对二维数组,a+i,不是指向具体存储单元而指向行。在二维数组中,a+i=ai=*(a+i)=&ai=&ai0,即它们的地址值是相等的。请读者仔细琢磨其概念。,注意,:,a+1,与,a0+1,不同,a+1,是,a,第,1,行分数组地址,*,(,a+1),是第,1,行第,0,列地址,*,*(,a+1),是元素,a10,a0,是第,0,行分数组名,a0+1,是,元素,a01,地址,*,(a0+1),是,元素,a01,形式,含义,内容,a ,&a0,二维数组名,0,行分数组地址,2000,a0,*(a+0),*a ,&a00,0,行一维数组名,0,行,0,列元素地址,2000,a0+1,*a+1,&a01 0,行,1,列元素地址,2002,a+1,&a1,1,行一维数组首地址,2008,a1,*(a+1) ,&a10,1,行一维数组名,1,行,0,列元素地址,2008,a1+3,*(a+1)+3,&,a13,1,行,3,列元素地址,2014,*(,a,2+3),*(*(,a,+2)+3),a,23,2,行,3,列元素,12,注意,:,a,和,a0,的地址均为,2000,但不等价,a+1,和,a0+1,不等。,2.,指向数组元素,和,指向分数组,的指针变量,(1),指向二维数组元素的指针变量,例,:,用指针变量输出数组元素的值。,main( ) int a34=1,2,3,4,5,6,7,8,9,10,11,12; int *,p,;,/*,定义,p,为指向整型的变量*,/,for (,p,=a0;,p,a0+12;,p,+,), if(,p,-a0)%4=0),printf(n); printf(%4d,*,p,); ,1 2 3 4,5 6 7 8,9 10 11 12,&a00,与,a0,等价,a0=,p=&a00,;,9,10,11,12,5,6,7,8,1,2,3,4,p,p+1,p+2,p+3,p+4,p+5,p+6,p+7,p+8,p+9,p+10,p+11,a0,指向二维数组的分数组的指针变量,应该指向整个一维数组。,指向整个一维数组的指针变量的定义如下:,基类型名,(*,指针变量名,),长度,;,指针变量名,先与*结合,,,再与,结合,。,先与*结合,表明是一个,指针变量,;,再与,结合,表明指针变量指向一个数组,。最后与,基类型结合,表明,数组元素的类型是基类型,。,(2),指向分数组的指针变量,注意,指针数组,和,指向数组的指针,的区别,int (*p)5;,/*,定义,p,为指向一维数组,(,元素为整型,个数为,5),的指针变量*,/,int *a5;,/*,定义,a,为数组,数组元素,(5,个,),均为指向整型的指针变量*,/,例如:,static,int a=10,b=20,c=30,d=40;,static,s34=1,2,3,4,5,6,7,8,9,10,11,12;,int,*q4,=,/*,指针数组*,/,int,(*p)4,;,/*,指向数组的指针*,/,int,*r,;,/*,指向整型的指针*,/,r=s0;,/* r=&s00 */,p=s;,/* p=&s0 */,1,5,2,6,3,7,4,8,9,10,11,12,p,p+1,p+2,s,r,r+1,r+2,10,20,30,40,a,b,c,d,&a,&b,&c,&d,q0,q1,q2,q3,q,s0,例 用指向,二维数组的分数组,的指针变量,按行输出二维数组中各元素的值。,main( ) int a34=1,2,3,4,5,6,7,8,9,10,11,12; int,*,q,;,/*,定义指向元素的指针变量,q*/,int,(*,p,)4;,/*,定义指向数组的指针变量,p*/,for(,p,=a,;,p,a,+3,;,p,+,), for(,q,=*,p,;,q,*,p,+4,;,q,+,),printf(%5d,*,q,);,printf(n); ,1 2 3 4,5 6 7 8,9,10 11 12,q q+2,p,p+2,例,用指向,分数组,的指针变量,输入多个字符串,将它们按行存储在二维字符数组中,然后输出全部字符串。,main( ),char a420;,char,(*p)20;,printf(“Input strings: n”);,for(,p=a; pa+4; p+,) gets(,*p,);,printf(“Output strings: n”);,for(,p=a; pa+4; p+,) printf(“,%s,”,*p,);,Input strings:,Zhang San,Li Si,Wang Wu,Welcome!,Output strings:,Zhang San, Li Si, Wang Wu, Welcome!,只有,字符数组,可以整体输入输出,p=a=&a0 *p=a0,,,a0,是一维字符数组名,(,字符串名,), *p,是一维字符数组,a,p,p,p,p,3.,用多维数组名和指针变量作函数参数,(1),用多维数组名作实参或形参。,如,: f(int a 4, int n);,(2),用指向元素的指针变量作实参或形参。,如,: f1(int *p);,(3),用指向分数组的指针变量作函数参数。,如,:f2(int (*q)4, int m);,例,用两个二维数组存储矩阵,调用函数求两个,矩阵之差,差矩阵存放在第一个实参数组中,用,指向分数,组的指针变量,作形参。矩阵输出也用函数实现。,#define N 4,sub(,int (*p1)N,int (*p2)N,int m,), int,*q1,*q2,(*u)N,;,u=p1+m;,for( ;,p1u,;,p1+,p2+,),for(,q1=*p1,q2=*p2,;,q1*p1+N,;,q1+,q2+,) *q1-=*q2;,print(,int (*p)N,int m), int,*q,(*u)N,;,u=p+m;,for( ;,pu;,p+,), for(q=*p;,q*p+N,;,q+,) printf(%4d,*q);,printf(n);,printf(n);,main(), int i, j,a N,=1,2,3,4,5,6,7,8 ;,int,b N,=10,20,30,40,50,60,70,80;,print(a,2); print(b,2);,sub(b,a,2);,print(b,2);,1 2 3 4,5 6 7 8,10 20 30 40,50 60 70 80,9 18 27 36,45 54 63 72,sub(,int (*p1)N,int (*p2)N,int m,), int,*q1,*q2,(*u)N,;,u=p1+m;,for( ;,p1u,;,p1+,p2+,),
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 商业管理 > 商业计划


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!