41943-00龚本灿-C语言程序设计教程第7章 2014指针_第1页
41943-00龚本灿-C语言程序设计教程第7章 2014指针_第2页
41943-00龚本灿-C语言程序设计教程第7章 2014指针_第3页
41943-00龚本灿-C语言程序设计教程第7章 2014指针_第4页
41943-00龚本灿-C语言程序设计教程第7章 2014指针_第5页
已阅读5页,还剩94页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第7章指针,指针是C语言的重要特色,也是一种非常实用的数据类型。合理使用指针,可以使程序简洁、高效、紧凑。但是,由于指针的概念很抽象,使用起来也比较灵活,因此,给初学者学习指针带来了较大的困难。本章将利用链表来保存“学生信息管理系统”中的学生信息,并在此基础上实现数据的添加、显示、修改和删除功能。,7.1指针概述7.2指向一维数组的指针7.3指向结构体的指针7.4返回指针的函数7.5动态内存分配7.6指针数组7.7指向二维数组的指针7.8指向函数的指针7.9指向指针的指针7.10案例分析与实现,【本章内容】,【学习目标】,掌握指针的基本概念、定义和引用掌握采用指针变量作为函数参数进行传址调用的方法掌握指向一维数组的指针及其算术运算掌握指向字符串的指针掌握指向结构体变量的指针掌握返回指针的函数了解动态数组、指针数组和指向二维数组的指针了解指向函数的指针和指向指针的指针了解链表的建立、插入与删除操作,7.1指针概述,7.1.1指针的概念内存是以字节为单位的连续的存储空间,每个字节都有一个唯一的编号(也称为地址)。如果在程序中定义了某个变量,C编译系统会根据该变量的数据类型,为其分配一定字节数的存储单元,该存储单元的第一个字节的地址称为变量的地址。,【例7-1】分析下面程序的执行结果。#includevoidmain()inta,b;floatc,d;doublee,f;printf(,1.指针变量的定义对变量的访问除了直接通过变量名外,还可以通过指针变量来进行。指针变量是一种专门用来存放地址的变量。定义指针变量的格式如下:基类型*指针变量名;例如:inta=2,*pa,*pb;charc=A,*pc;doubled=2.1,*pd;pa=,7.1.2指针变量的定义和引用,要注意以下几个问题,(1)对指针变量赋值时,另一变量的类型必须和指针变量的基类型相同。例如,下面的赋值是错误的:int*pa;floatf;pa=。,C语言提供了一个指针运算符“*”,通过它来访问指针变量所指向的变量。,2.指针变量的引用,【例7-2】分析下面程序的执行结果。#includevoidmain()inta=5,b,*pa;pa=,语句b=*pa;中,*pa的含义是:访问pa所指向的变量,因此,该语句等价于:b=a;。由于*pa实际上取了pa所指向的存储单元的内容(即5),将5赋给b,因此,指针运算符也称为“取内容运算符”。,【例7-3】从键盘输入两个整数x和y,求这两个数的最大值。#includevoidmain()intx,y,max,*px,*py;px=,7.1.3指针变量作为函数参数,函数的参数不仅可以是整型、实型、字符型等类型,还可以是指针类型。若函数的形参是指针类型,调用该函数时,对应的实参必须是同类型变量的地址或同类型的指针变量。,【例7-4】编写实现两个整数交换的自定义函数swap(),要求形参采用指针变量,在main()函数中输入两个整数,调用swap()函数后输出结果。,#includevoidmain()intm,n;voidswap(int*pm,int*pn);printf(请输入两个整数:);scanf(%d%d,问题1.不采用指针变量?2.如果将int*temp;改为int*temp;?,【程序说明】,(1)由于swap()函数的形参为指针变量,这种函数调用方式称为传址调用。与之相反,如果函数的形参为一般变量,则称为传值调用。(2)采用传址调用时,如果在函数中修改了指针变量所指向的存储单元的内容,则实参变量的值也会相应发生改变,这是采用指针变量带来的好处。,【归纳总结】,(1)“*”号在不同的场合有不同的含义。定义指针变量时的“*”号为指针类型标识符,表示所定义的变量为指针变量;引用指针变量时的“*”号为指针运算符,表示访问指针变量所指向的变量。(2)给指针变量赋值时,只能赋地址量,且指针变量前不能加“*”。(3)传址调用时,能够通过形参指针变量来修改实参的值,这是传址调用有别于传值调用的地方,也是采用指针变量带来的好处之一。,7.2.1指向一维数组的指针变量的定义,7.2指向一维数组的指针,例如:inta10=10,20,30,40,50,60,70,80,90,100,*p;使指针变量指向数组元素:(1)p=a;(2)p=C语言规定,数组名a表示数组的首地址。数组名a是地址常量,不能对其赋值。,1.指针变量加减整数例如:inta10,*p;p=a;p=p+9;执行语句p=a;后,存在以下对等关系:(1)p+9、a+9和(或+p;)相当于:p=p+1;作用是:使指针变量指向当前位置的下一个存储单元。3.两个指针变量相减两个指针变量相减,其差等于两个指针变量所指向的存储单元之间的元素个数。例如:inta10,*p,*q;p=,7.2.3指针变量的应用举例,【例7-5】写出下面程序的执行结果。,#includevoidmain()inti,a6=1,2,3,4,5,6,*p=a;for(i=0;i6;i+)printf(%d,ai);printf(n);for(i=0;i6;i+)printf(%d,pi);printf(n);for(i=0;i6;i+)printf(%d,*(a+i);printf(n);for(i=0;i6;i+)printf(%d,*(p+i);printf(n);for(p=a;pstr2n);elseif(strcmp1(str1,str2)math;由于(*p)表示p所指向的结构体变量,相当于stu1,因此,(*p).math相当于stu1.math。p-math形象地表示了p所指向的结构体变量的成员math。,【例7-9】采用3种方式输出“学生信息管理系统”中某个学生的信息。#includevoidmain()structstu_typecharnum15;/学号charname10;/姓名intage;/年龄intc;/C语言分数intmath;/数学分数inten;/英语分数intsum;/总分floatave;/平均分;structstu_typestu1=2011110101,张小天,22,60,70,75,*p=,printf(学生学号t姓名t年龄tC语言t数学t英语t总分t平均分n);printf(%-16s%st%dt%dt%dt%dt%dt%5.1fn,stu1.num,,stu1.age,stu1.c,stu1.math,stu1.en,stu1.sum,stu1.ave);printf(%-16s%st%dt%dt%dt%dt%dt%5.1fn,(*p).num,(*p).name,(*p).age,(*p).c,(*p).math,(*p).en,(*p).sum,(*p).ave);printf(%-16s%st%dt%dt%dt%dt%dt%5.1fn,p-num,p-name,p-age,p-c,p-math,p-en,p-sum,p-ave);,7.3.2指向结构体数组的指针,【例7-10】采用指针方式输出“学生信息管理系统”中多个学生的信息。#includevoidmain()structstu_typecharnum15;/学号charname10;/姓名intage;/年龄intc;/C语言分数intmath;/数学分数inten;/英语分数intsum;/总分floatave;/平均分;,structstu_type*p,stu3=2011110101,张小天,22,60,70,75,2011110102,王小红,22,65,75,75,2011110103,李小兵,21,68,78,75;printf(学生学号t姓名t年龄tC语言t数学t英语t总分t平均分n);for(p=stu;psum=p-c+p-math+p-en;p-ave=(float)p-sum/3;printf(%-16s%st%dt%dt%dt%dt%dt%5.1fn,p-num,p-name,p-age,p-c,p-math,p-en,p-sum,p-ave);,7.4返回指针的函数,定义返回指针的函数时,只需要在函数名前加一个“*”即可,一般形式如下:类型名*函数名(参数列表)函数中通过return语句返回的值必须是指针。,【例7-11】编写一个求子串的函数substr(char*s,intn1,intn2),从s指向的字符串中提取序号在n1n2之间的字符组成一个新的字符串,返回这个新串的首地址。编程思路:由于函数要返回新串的首地址,因此,函数的类型应该设为char*。,#includechar*substr(constchar*s,intn1,intn2)staticchara80;inti,j=0;for(i=n1-1;i=n2-1;i+,j+)aj=si;aj=0;returna;voidmain()chars80,*sub;intn1,n2;printf(请输入原字符串:);scanf(%s,s);printf(请输入起止位置:);scanf(%d%d,【程序说明】,(1)函数substr()的形参s用来指向原字符串,在数据类型的前面加了一个const,它是“只读”限定符,表示该变量为只读变量,其值在程序中不允许被修改。(2)函数substr()中定义了一个字符数组a,用来存放得到的新串,处理结束后,通过returna;语句将新串的首地址返回给main()函数,并赋给指针变量sub,这样sub就指向了数组a。(3)函数substr()中定义数组a时,static不能省略。,7.5动态内存分配,“自由存储区”:可以在程序运行的过程中,根据实际需要来临时申请的内存空间。在C语言中,定义数组时必须明确地说明其大小,但在实际编程中,数组的大小最初不能预料,只有在程序运行过程中根据具体情况才能确定。例如,在“学生信息管理系统”中,将学生数组定义为stu100。,动态内存分配函数如表7-1所示,使用这些函数时需要包含头文件:stdlib.h。,【函数说明】,上述函数的返回值类型均为void*,这并不是说函数调用后无返回值,而是返回一个基类型为void的指针,它指向所分配存储区的首地址。调用函数时应根据实际情况,采用强制类型转换方法将其转换为所需的类型。(2)分配的内存在使用完毕后可用free()函数来释放,如果程序中未释放,则该内存空间会一直占用,直到程序运行结束后才会被操作系统释放。,【例7-12】编写一个求子串的函数substr(char*s,intn1,intn2),从s指向的字符串中提取下标范围在n1n2之间的字符组成一个新的字符串,返回这个新串的首地址。,#include#includechar*substr(constchar*s,intn1,intn2)char*p=(char*)malloc(n2-n1+2);/申请存储空间inti,j=0;for(i=n1;iname);printf(请输入年龄:);scanf(%d,【程序说明】,(1)malloc()函数的实参为sizeof(structstu_type)*n,其中,sizeof(structstu_type)表示一个学生信息所占用的内存单元的大小,需要分配n个这样的内存单元。(2)malloc()函数的返回值类型为void*,而指针p的类型为structstu_type*,两者不匹配,因此,使用了强制类型转换方法对malloc()函数的返回值进行类型转换。(3)程序中定义了两个指针变量p和p1。每输入一个学生的数据,p1将加1,指向下一个内存单元,输入结束后,p1指向了所分配的内存空间之外。因此,可以用p来依次输出每个学生的信息,但不能用p1。(4)只能通过指针来引用“自由存储区”中的数据。,【例7-13】采用动态内存分配方法改写例7-11的程序,按用户实际的子串长度来分配内存空间。,#include#includechar*substr(constchar*s,intn1,intn2)char*p=(char*)malloc(n2-n1+2);inti,j=0;for(i=n1-1;i=n2-1;i+,j+)pj=si;pj=0;returnp;,voidmain()chars80,*sub;intn1,n2;printf(请输入原字符串:);scanf(%s,s);printf(请输入起止位置:);scanf(%d%d,/释放sub所占用的空间,【程序说明】,(1)在例7-11的substr()函数中,数组a用来存储子串,其大小被固定为80。动态内存分配函数可以根据子串的实际长度来申请内存空间。(2)采用malloc()函数申请内存空间时,因为还要存储字符串结束标志,因此,申请内存空间的大小为n2-n1+2。,7.6指针数组,若一个数组的所有元素都是指针类型,则这样的数组叫做指针数组。指针数组中每个元素都相当于一个指针变量,一维指针数组的定义格式如下:基类型*指针变量名数组长度;例如:int*p3,i=1,j=2,k=3;p0=,【例7-14】对参加北京奥运会的国家名称按由小到大的顺序排序后输出。,本例采用两种方法来实现,并比较他们的优劣,以便体会到采用指针的好处。方法(1)采用二维数组。编程思路如下:一个国家的名称是一个字符串,可以用一个一维字符数组来保存,要存储多个国家的名称就需要用二维字符数组。假设最多有N个国家参加,所有国家的名称最长为LEN个字符,则二维数组的定义如下:charnameNLEN。采用“冒泡排序法”对这些国家的名称进行排序。,#include#include#defineN4#defineLEN20voidmain()inti,j;chartempLEN,nameNLEN=“China”,”America”,”Australia”,”Japan”;for(i=1;i0)/两两比较strcpy(temp,namej);strcpy(namej,namej+1);strcpy(namej+1,temp);for(i=0;iN;i+)printf(%sn,namei);,【程序说明】,(1)二维数组nameNLEN可以看成由多个一维数组组成。首先将name看成是由name0、name1、name2和name3这4个元素组成的一维数组,name是它的数组名。再将这4个元素分别看成是由LEN个字符组成的一维数组。(2)排序过程中,交换时需要移动整个字符串,因此,排序的速度较慢。,方法(2)采用指针数组#include#include#defineN4voidmain()inti,j;char*temp,*nameN=China,America,Australia,Japan;for(i=1;i0temp=namej;namej=namej+1;namej+1=temp;for(i=0;iN;i+)printf(%sn,namei);,方法(2)在执行速度上优于方法(1)。排序时,并不移动字符串的位置,只是改变了指针数组中各元素的指向。排序结束后,name0指向最小的字符串,nameN-1指向最大的字符串。,7.7指向二维数组的指针,7.7.1二维数组的地址C语言中,二维数组可以看成由多个一维数组组成。例如:inta34=0,1,2,3,4,5,6,7,8,9,10,11;可以按下面的方式来看待数组a:(1)二维数组a可以看成是由a0、a1、a2这3个元素组成的一维数组,a是它的数组名。a0、a1和a2这3个元素又可以分别看成是一个一维数组。以a0为例,a0是一维数组的数组名,它包含4个数组元素:a00、a01、a02和a03。,(2)对二维数组a而言,数组名a(即a+0)指向数组的首元素a0,a+1指向元素a1,a+2指向元素a2。相对于a而言,a+1跨过了二维数组的一行数据,所以,a、a+1和a+2称为行地址。(3)对第一行而言,a0、a01、a0+2称为列地址。,通过以上分析可以看出:a+i指向ai,是ai的地址(即int(*p)4;p=a;不能给行指针赋列地址,如下面的赋值语句是错误的:p=a0;,2.列指针定义列指针的方法与定义简单指针变量的方法相同,对列指针进行赋值时只能赋列地址。例如:inta34=0,1,2,3,4,5,6,7,8,9,10,11;int*p;/定义列指针变量p=a0;/赋列地址不能给列指针赋行地址,如下面的赋值语句是错误的:p=a;,【例7-15】有一个MM方阵,采用列指针输入方阵的数据,采用行指针输出方阵的数据。#include#defineM3voidmain()inti,j,aMM,*q,(*p)M;printf(请输入方阵数据:);for(q=a0;qa0+M*M;q+)scanf(%d,q);p=a;for(i=0;iM;i+)for(j=0;jscore=score;/给分数赋值newN-next=NULL;/给next指针赋值,7.10.3学生信息链表的操作,(2)将新结点链接到表尾。对于空表,程序代码如下:head=newN;对于非空表,程序代码如下:tail-next=newN;,(3)将链表的尾指针tail指向新结点,以便添加新的结点。程序代码如下:tail=newN;,向空链表中增加一个新结点的过程如图7-15所示。向非空链表中增加一个结点的过程如图7-16所示。,创建学生链表的函数代码,#include#include#includestructstu_nodecharnum15;intscore;structstu_node*next;structstu_node*stu_create()/建立链表函数structstu_node*head,*newN,*tail;charnum15;intscore;/创建第一个结点,并输入数据printf(请输入学生的学号和分数(学号为0表示结束):n);scanf(%s%d,num,/让尾指针指向新结点,/继续创建后续结点,并输入数据while(1)printf(请输入学生的学号和分数(学号为0表示结束):n);scanf(%s%d,num,编程思路:定义一个指针变量p,并将链表的头指针head赋给p。开始时,p指向链表的第1个结点,输出该结点中的数据;然后通过p=p-next;语句,将指针变量p移到下一个结点,再输出数据,直至输出最后一个结点的数据为止。数据输出的过程如图7-17所示。,2输出学生信息链表中的数据,voidstu_print(structstu_node*head)/输出链表数据的函数structstu_node*p=head;/使指针p指向链表的第一个结点if(p=NULL)/如果链表为空printf(学生信息为空!n);return;printf(学号t分数n);/打印表头while(p!=NULL)printf(%st%dn,p-num,p-score);/输出p=p-next;/使指针p指向当前结点的下一个结点在main()函数中,上述函数的调用语句如下:stu_print(head);,3按学号修改学生的分数,编程思路:定义一个指针变量p,并将链表的头指针head赋给p。根据输入的学号,通过循环语句,从链表的第一个结点开始,查找该学生,直至找到该学生或链表结束为止。如果找到了,则输入该学生的信息,并保存到当前结点中。,voidstu_modify(structstu_node*head)charnum15;structstu_node*p=head;/使指针p指向链表的第一个结点if(head=NULL)printf(学生信息为空!n);return;printf(请输入要修改的学生的学号:);scanf(%s,num);while(p!=NULL,4按学号删除学生信息,编程思路:(1)定义两个指针变量p和p1,其中,p用来指向待删结点,p1用来指向待删结点的前一个结点。开始时将链表的头指针head赋给p。(2)查找待删结点。根据输入的学号,通过循环语句,从链表的第一个结点开始,在链表中查找要删除的学生,直至链表结束或找到该学生为止。查找过程中指针变量p和p1都要移动,p1始终指向p的前一个节点。,分两种情况:如果待删结点是第一个结点,则使头指针指向待删结点的下一个

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论