ImageVerifierCode 换一换
格式:DOCX , 页数:44 ,大小:69.46KB ,
资源ID:7645164      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/7645164.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(C#程序设计第10章指针.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

C#程序设计第10章指针.docx

1、C#程序设计 第10章指针第10章 指针指针是C语言最灵活的部分,它充分体现了C语言简洁、紧凑、高效等重要特色。可以说,没掌握指针就没掌握C的精华。指针部分概念复杂,使用灵活,初学时常会出错,学习过程中应十分小心,多思考、多比较、多上机,尽量采用图示帮助分析问题与解决问题。101 变量的地址和指针变量1011 变量的地址及变量存取方式指针之所以难学是因为它与内存有着密切的联系,简单地说,指针就是内存地址。这里首先要区分三个比较接近的概念:名称;内容(值)和地址。名称是给内存空间取的一个容易记忆的名字;内存中每个字节都有一个编号,就是“地址”;在地址所对应的内存单元中存放的数值即为内容或值。为了

2、帮助读者理解三者之间的联系与区别,我们不妨打个比方,有一座教师办公楼,各房间都有一个编号,如,101,102,201,202,。一旦各房间被分配给相应的职能部门后,各房间就挂起了部门名称:如,电子系、计算机系、机械工程系等,假如电子系被分配在101房间,我们要找到电子系的教师(内容),可以去找电子系(按名称找),也可以去找101房间(按地址找)。类似地,对一个存储空间的访问既可以指出它的名称,也可以指出它的地址。C语言规定编程时必须首先说明变量名、数组名,这样编译系统就会给变量或数组分配内存单元。系统根据程序中定义的变量类型,分配固定长度的空间,微机的C编译系统为整型变量分配2个字节,为实型变

3、量分配4个字节,为字符型变量分配1个字节。例如:int i,j,k;是程序中定义的三个整型变量,C编译系统在编译过程中为这三个变量分配空闲的内存空间,并记录下各自对应的地址,如图10-1所示。2000(i)120022004(j)82006(k)9图10-1从用户角度看,访问变量i和访问地址2000是对同一空间的两种访问形式;而对系统来说,对变量i的访问归根结缔还是对地址的访问,因而若在程序中执行如下赋值语句:i=1,j=8,k=9;编译系统会将数值1,8,9依次填充到地址为2000,2004,2006内存空间中。系统对变量访问形式分成两种:直接访问按变量地址存取的变量值的方式称为“直接访问”

4、方式。说明:用变量名对变量的访问也属于直接访问,因为在编译后,变量名和变量地址之间有对应关系,对变量名的访问系统自动转换成利用地址对变量的访问。间接访问将变量的地址存放在一种特殊变量中,利用这个特殊变量进行访问。如图10-2所示,特殊变量p存放的内容是变量i的地址,利用变量p来访问变量i的方法称为“间接访问”。 图10-2在C语言中,如果变量p中的内容是另一个变量i的地址,则称变量p指向变量i,或称p是指向变量i的指针变量,形象地用图10-2所示的箭头表示。由此可以得出结论:变量的指针即为变量的地址,而存放其它变量地址的变量是指针变量。1012 指针变量的定义和指针变量的基类型1指针变量定义基

5、类型 *变量名2示例int *pointer_1,*pointer_2;float *f;char *pc;3说明 C语言规定所有变量必须先定义后使用,指针变量也不例外,为了表示指针变量是存放地址的特殊变量,定义变量时在变量名前加指向符号“*”。 定义指针变量时,不仅要定义指针变量名,还必须指出指针变量所指向的变量的类型即基类型,或者说,一个指针变量只能指向同一数据类型的变量。由于不同类型的数据在内存中所占的字节数不同,如果同一指针变量一会儿指向整型变量,一会儿指向实型变量,就会使该系统无法管理变量的字节数,从而引发错误。 示例第一行定义了两个指向整型数据的指针变量pointer_1,poin

6、ter_2,第二行定义了指向实型数据的指针变量f,第三行定义了指定字符型数据的指针变量pc。1013指针变量赋值将指针变量指向某个变量的方法是将被指变量的地址赋值给该指针变量,这里就要用到取址运算符“&”。例如int i;int *p;p=&i;上述命令的执行将使指针变量p指向变量i。如图10-2所示。注意:虽然变量的地址&i是一个整型数据,但一般情况下不要给指针变量送一个整型常量,如p=1000是不允许的,这是因为变量的地址是由编译系统分配的,用户一般不知道,也不必知道。1014 指针变量引用 1指针运算符&:取地址运算符*:指针运算符(间址访问运算符)2指针变量引用举例【例10-1】通过指

7、针变量访问整型变量main()int i=100,j=10;int *pi,*pj;pi=&i; /*将pi指向i*/pj=&j; /*将pj指向j*/printf(“%d,%dn”,i,j); /*直接访问变量i,j*/printf(“%d,%d”,*pi,*pj);/*间接访问变量i,j*/运行结果 100,10100,10程序说明: int *pi,*pj;语句定义了变量pi,pj是指向整型变量的指针变量,但没指定它们指向哪个具体变量。 pi=&i; pj=&j; 语句确定了pi,pj的具体指向,pi指向i,pj指向j。不能误写成:*pi=&i,*pj=&j; printf(“%d,%d

8、n”,i,j);语句通过变量名直接访问变量的方法,这是我们最常用的手段。 printf(“%d,%d”,*pi,*pj);语句通过指向变量i,j的指针变量来访问变量i,j的方法,*pi表示变量pi所指向的单元的内容,即i的值;*pj表示变量pj所指向的单元的内容,即j的值,因而两个printf语句输出的结果均为变量i,j所对应的值。需要明确的是这里的*是对变量pi,pj所指向单元的值的引用;而int *pi,*pj;语句处pi,pj没有具体的指向,*定义了pi,pj属于指针变量,而非间址运算符。3“*”与“&”运算符的进一步说明 如果已执行了”pointer_1=&a;”语句,则&*point

9、er_1的值是&a。因为“*”与“&”运算符的优先级相同,并且是自右向左结合,所以先进行*pointer_1的运算得到变量a,再进行&运算得到的值为变量a的地址。 如果已执行了“a=100;”语句,则*&a的值是a即100。因为先进行&a运算得到a的地址,再进行*运算,得到a地址的内容a。 指针加1,不是纯加1,而是加一个所指变量的字节个数。例如int *p1,a=100;p1=&a;p1+;假如a的地址是2000,p1+后p1的值为2002,而非2001,如图10-3所示,如果p1是指向实型单精度变量的指针变量,其初值为2000,则p1+后的值为2004。10050图10-31015 指针变

10、量作为函数的参数函数的参数不仅可以是整型、实型等基本数据类型,还可以是指针类型。它的作用是把地址传给被调函数。下面通过一个示例来说明。【例10-2】输入a和b,按从小到大的顺序输出。void swap(int *p1,int *p2)int t;t=*p1;*p1=*p2;*p2=t;main()int a,b;int *q1,*q2;q1=&a;q2=&b;scanf(“%d,%d”,q1,q2);printf(“%d,%d”,q1,q2);if(ab)swap(q1,q2);printf(“%d,%d”,a,b);printf(“%d,%d”,q1,q2);运行情况9,5_ _,_ _5,

11、9_ _,_ _其中_ _表示q1,q2的地址值,从程序的输出结果可以看出,a,b的值发生交换,但q1,q2的值并未交换。程序说明在被调函数swap中,将形参p1,p2说明成指针型变量,该函数的作用是交换两个变量的值。程序运行时,先执行main函数,输入两个数9,5给变量a,b。将a,b的地址分别赋值指针变量q1,q2,然后执行if语句,由于ab,因此执行swap函数,在调用过程中,首先将实参q1,q2的值传递给形参p1,p2,经虚实结合后,形参p1指向变量a,形参p2指向变量b,如图10-4(a)所示。接着执行swap函数体,将*p1(a)与*p2(b)中的值交换,互换后的情况如图10-4(

12、b)所示。函数调用结束后,形参p1,p2将释放,如图10-4(c)所示。最后在main函数中输出的a和b的值即为交换后的值(a=5,b=9),由于q1,q2在调用swap函数前后没有改变, main函数两次输出的q1,q2的值均相等。 图10-4不正确的使用及处理 swap函数中的中间变量定义成指针类型变量void swap(int *p1,int *p2)int *t;t=*p1;*p1=*p2;*p2=t;函数将出现语法错误,原因是由于变量t无指向,所以不能引用变量*t。 被调函数中的地址交换void swap(int *p1,int *p2)int *t;t=p1;p1=p2;p2=t;

13、swap函数调用结束后,变量a和b中的值没有交换。原因是函数swap交换了变量p1,p2的值,无法通过值传递形式返回主函数中的p1,p2。 利用普通变量作函数参数void swap(int x,int y)int t;t=x;x=y;y=t;main()swap(a,b)主函数中直接将a,b作为实参传递给swap函数,形参数据在swap函数中交换后并不返回主函数。102数组的指针与指向数组的指针变量数组是由若干相同类型的元素构成的有序序列,这些元素在内存中占据了一组连续的存储空间,每个元素都有一个地址,数组的地址指的是数组的起始地址,这个起始地址也称为数组的指针。1021 指向数组的指针如果一

14、个变量中存放了数组的起始地址,那么该变量称为指向数组的指针变量,指向数组的指针变量的定义遵循一般指针变量定义规则。它的赋值与一般指针变量的赋值相同。如果有以下定义int a10,*p;p=&a0;注意,如果数组为int型,则指针变量必须指向int 类型。上述语句组的功能是将指针变量p指向a0,由于a0是数组a的首地址,所以指针变量p指向数组a。如图10-5所示。 a pa0a1图10-5C语言规定,数组名代表数组的首地址,因此,下面两个语句功能相同;p=a;p=&a0;允许用一个已经定义过的数组的地址作为定义指针时的初始化值。例如float score20;float *pf=score;注意

15、:上述语句的功能是将数组score的首地址赋给指针变量pf,这里的*是定义指针类型变量的说明符,而非指针变量的间址运算符,不是将数组score的首地址赋给*pf。1022 通过指针引用数组元素已知指向数组的指针后,数组中各元素的起始地址可以通过起始地址加相对值的方式来获得,从而增加了访问数组元素的渠道。C语言规定,如果指针变量p指向数组中的一个元素,则p+1指向同一数组中的下一个元素(而不是简单地将p的值加1),如果数组元素类型是整型,每个元素占2个字节,则p+1意味着将p的值加2,使它指向下一个元素。因此,p+1所代表的地址实际上是p+1*d,d是一个数组元素所占的字节数(对整型数组,d=2

16、;对实型数组,d=4;对字符型数组,d=1)。1地址表示法当p定义为指向a数组的指针变量后,就会产生对同一地址不同的表示方法。例如数组元素a5的地址有三种不同的表示形式p+5, a+5, &a52访问表示法与地址表示法相对应,访问数组元素也有多种表示法。例如数组元素a5可通过下列三种形式访问*(p+5), *(a+5), a53指针变量带下标指向数组的指针变量可以带下标,如p5与*(p+5)等价。4指针变量与数组名的引用区别指针变量可以取代数组名进行操作,数组名表示数组的首地址,属于常量,它不能完成取代指针变量进行操作。例如,设p为指向数组a的指针变量,p+可以,但a+不行。5+与+i不等价用

17、指针变量对数组逐个访问时,一般有两种方式,*(p+)或*(p+i),表面上这两种方式没多大区别,但实际上有很大差异,像p+不必每次都重新计算地址,这种自加操作比较快的,能大大提高执行效率。根据以上叙述,引用一个数组元素,可以用两个方法: 下标法,通过数组元素序号来访问数组元素,用ai形式来表示。 指针法,通过数组元素的地址访问数组元素,用*(p+i)或*(a+i)的形式来表示。【例10-3】任意输入10个数,将这十个数按逆序输出。 用下标法访问数组main()int a10,i; for(i=0;i=0;i-)printf(“%d”,ai);2 数组名访问数组main()int a10,i;

18、for(i=0;i=0;i-)printf(“%d”,*(a+i);3 指针变量访问数组A)main()int a10,i,*p;for(i=0;i=0;p-)printf(“%d”,*(p+i);B)main()int a10,I,*p;p=a;for(i=0;i=a;p-)printf(“%d”,*p);将上述三种算法比较如下: 例10-3中,A)执行效率是相同的,编译系统需要将ai转换成*(a+i)处理的,即先计算地址再访问数组元素。 B)执行效率比其它方法快,因为它有规律地改变地址值的方法(p-)能大大提高执行效率。 要注意指针变量的当前值。请看下面程序,分析其能否达到依次输出10个数

19、组元素的目的,为什么?main()int a10,i,*p;p=a;for(i=0;i10;i+)scanf(“%d”,p+);printf(“n”);for(i=0;i10;i+,p+)printf(“%d”,*p);有关指针变量运算下面将进一步加以说明。如果指针变量p指向数组a,比较以下表达式的含义。1 达式*p+,由于+与*运算符优先级相同,结合方向为自右向左,故*p+的作用是先得到*p的值,再使p+1p。同样表达式*p-的作用是先得到*p的值,再使p-1p。2 达式*+p,先使p+1p,再得到*p的值。同样表达式*-p的作用是先使p-1p,再得到*p的值。3 表达式(*p)+表示p所指

20、向的数组元素值(*p)加1,变量p的值不会改变。同样,(*p)- ,表示p所指向的数组元素的值(*p)减1。1023 数组名作为函数参数正如在函数部分所述,数组名也可作为函数的参数,如:main() sort(int x,int n) int a10; sort(a,10); 由于数组名代表数组的首地址,故在函数调用时(sort(a,10);)按“虚实结合”的原则,把以数组名a为首地址的内存变量区传递给被调函数中的形参数组x,使得形参数组x与主调函数的数组a具有相同的地址,故在函数sort中这块内存区中的数据发生变化的结果就是主调函数中数据的变化,如图10-6所示,这种现象好像是被调函数有多个

21、值返回主函数,实际上“单向”传递原则依然没变。 图10-6有了指针的概念后,对数组名作为函数参数可以有进一步的认识,实际上,能够接受并存放地址值的形参只能是指针变量,C编译系统都是将形参数组名作为指针变量来处理的。因此函数sort的首部也可以写成sort(int *x,int n)在函数调用过程中,x首先接受实参数组a的首地址,也就指向了数组元素a0,前面已经讲过,指针变量x指向数组后,就可以带下标,即xi与*(x+i)等价,它们都代表数组中下标为i的元素。由于函数参数有实参、形参之分,所以数组指针作为函数参数分以下四种情况:1 形参、实参为数组名;在第8章中已作详细介绍。 2 形参是指针变量

22、,实参是数组名; 【例10-4】用选择法对10个整数排序。void sort (int *b,int n) /*形参b为指针变量*/int i,j,k,t;for(i=0;in-1;i+)k=i;for(j=i+1;jn;j+)if(*(b+j)*(b+k) k=j;if(k!=i)t=*(b+k);*(b+k)=*(b+i);*(b+i)=t;main()int a10,i;for(i=0;i10;i+)scanf(“%d”,&ai);sort(a,10) /*实参为数组名*/for(i=0;i10;i+)printf(“%d,”,ai);printf(“n”);运行结果1 -2 12 9

23、56 100 3 1 10 256,-2,0,1,2,3,9,10,12,100程序分析形参是指针变量b,函数调用时,它接受数组a的首地址,即指针变量b指向数组a,表达式*(b+i)表示数组中第i个元素;通过函数sort改变了数组元素的顺序,返回主函数后,可以输出按从小到大排序后的数组。3 形参、实参均为指针变量【例10-5】将数组a中前n个元素按相反顺序存放。设n=6,解此算法要求将a0与a5交换,a1与a4交换,将a2与a3交换。通过分析,我们发现被交换的两个数组元素下标的和为n-1,今用循环来处理此问题,设定两个“位置指针变量”i和j,i初值为x,j的初值为x+n-1,将ai与aj交换,

24、然后将i增加1,j减少1,再交换ai与aj,直到ij结束循环,如图10-7所示。2468101214161820ij1210864214161820图10-7void inv(int *x,int n)/*形参x为指针变量*/int *p,*i,*j,temp;for(i=x,j=x+n-1;ij;i+,j-)temp=*i;*i=*j;*j=temp;main()int i,n,a10=2,4,6,8,10,12,14,16,18,20;int *p;printf(“the original array:n”);for(i=0;i10;i+)printf(“%d,”,ai);printf(“

25、n”);p=a; /*给实参指针变量p赋值*/printf(“input to n:n”);scanf(“%d”,&n);inv(p,n); /*实参p为指针变量*/printf(“the array after invented:n”);for(p=a;pa+10;p+)printf(“%d,”,*p);printf(“n”);运行结果the original array:2,4,6,8,10,12,14,16,18,20,input to n:612,10,8,6,4,2,14,16,18,20程序分析:若实参为指针变量,在调用函数前必须给指针变量赋值,使它指向某一数组,注意本例中的第一个

26、“p=a;”语句。本程序显示前6个整数按逆序排列后的结果。想一想,能否对算法进行修改,要求只使用一个位置指针变量?4 形参是数组名,实参为指针变量将【例10-5】稍作改动,形如void inv(int x ,int n) /*形参x为数组名*/ main()int a10,n;int *p; p=a; /*给实参指针变量p赋值*/inv(p,n); /*实参p为指针变量*/ 说明:调用函数前必须给实参指针变量赋值。在函数inv中,既可以用下标法xi,也可以用指针法*(x+i)处理第i个数组元素,处理结果在返回主函数后有效。1024 指向多维数组的指针和指针变量用指针变量可以指向一维数组,也可以

27、指向多维数组。多维数组的首地址称为多维数组的指针,存放这个指针的变量称为指向多维数组的指针变量。多维数组的指针并不是一维数组指针的简单拓展,它具有自己的独特性质,在概念上和使用上,指向多维数组的指针比指向一维数组的指针更复杂。1 多维数组的地址多维数组的首地址是这片连续存储空间的起始地址,它既可以用数组名表示,也可以用数组中第一个元素的地址表示。以二维数组为例,设有一个二维数组s34,其定义如下int s34=0,2,4,6,1,3,5,7,9,10,11,12;这是一个3行4列的二维数组,如图10-8所示,s数组包含3行,即由3个元素组成:s0,s1,s2。而每一行又是一个一维数组,包含4个

28、元素,如,s0包含s00,s01,s02,s03;。s020000200222004420066s120081201032012520147s220169201810202011202212图10-8从二维数组的角度看,s代表二维数组的首地址,也是第0行的首地址,s+1代表第1行的首地址,从s0到s1要跨越一个一维数组的空间(包含4个整型元素,共8个字节)。若s数组首地址为2000,则s+1为2008;s+2代表第2个一维数组的首地址,值为2016。s0,s1,s2既然是一维数组名,C语言又规定数组名代表数组的首地址,因此s0 表示第0行一维数组的首地址,即&s00;s1 表示第1行一维数组的首地址,即&s10;s2 表示第2行一维数组的首地址,即&s20。s0+1表示第0行一维数组第1个元素的地址&s01;s1+2=&s12。对各元素内容的访问也可以写成*(s0+1),*(s1+2)。2 行转列的概念经过上述分析,我们知

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1