C语言程序设计课件:指针_第1页
C语言程序设计课件:指针_第2页
C语言程序设计课件:指针_第3页
C语言程序设计课件:指针_第4页
C语言程序设计课件:指针_第5页
已阅读5页,还剩28页未读 继续免费阅读

下载本文档

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

文档简介

指针第一节指针的概念第二节指针与数组的关系第三节指向结构体类型数据的指针第四节项目任务:用动态内存分配实现输入输出管理第一节指针的概念计算机硬件系统中有两个重要的硬件:CPU和内存。程序是在CPU的控制下运行的,而程序执行时需要处理各种数据,这些数据是被存放在内存中的。为了便于管理,内存空间被划分成若干个大小相同(1个字节)的存储单元,并为每一个存储单元安排一个编号,这个编号被称为内存地址,例如:在程序中,如果定义了如下的整型变量:intx,y;在编译时就给这两个变量分配了内存单元。假定变量x占用编号为20A0(十六进制)和20A1的两个连续的内存单元,变量y占用20A2和20A3两个连续的内存单元。若程序中有如下语句:x=15;y=30;则系统实际上就把15送到地址为20A0开始的两个连续单元中,把30送到地址为20A2开始的两个连续单元中。作为一类特殊的变量,指针就像一个指示器,它告诉程序在内存的什么地方可以找到数据。当然,数据在内存中占几个单元是由数据的类型决定的,如字符型变量分配1个字节,整型变量分配2个连续字节,单精度实型变量分配4个连续字节,双精度实型变量分配8个连续字节,指针指向的是相应数据在内存中存放空间的第1个单元的地址。因此,我们把地址叫做“指针”。存放地址的变量,称为指针变量,所以,指针就是地址。一、指针和指针变量(一)指针──即地址一个变量的地址称为该变量的指针。通过变量的指针能够找到该变量。(二)指针变量──指针变量是一种专门存放其他变量在内存中的地址的特殊变量。它的值是变量的地址(而非变量的值!)。指针与指针变量的区别,就是变量值与变量的区别。由此可见,指针就是“内存单元的地址”。指针指向一个内存单元。指针变量就是地址变量。地址(指针)也是数据,可以保存在一个变量中。保存地址(指针)数据的变量称为指针变量。而变量的指针就是“变量的地址”。变量的指针指向一个变量对应的内存单元。既然指针变量的值是一个地址,那么这个地址不仅可以是变量的地址,也可以是其他数据类型的地址,如数组的首地址等。二、指针变量的定义指针变量的定义方法如下:类型声明符*指针变量名;其中,“*”表示这是一个指针变量,变量名即为定义的指针变量名,类型声明符表示该指针变量所指向的变量的数据类型。例如:int*pointer1,*pointer2;/*pointer1和pointer2是两个指针变量,指向的数据类型为整型*/float*pointer3;/*pointer13是指针变量,指向的数据类型为浮点型*/char*pointer4;/*pointer4是指针变量,指向的数据类型为字符型*/在定义指针变量时要注意两点:1、标识符前面的"*",表示该变量为指针变量。2、一个指针变量只能指向同一个类型的变量。注意:指针变量也是一种变量,与我们前面介绍过的普通变量相比,它只不过是一种特殊的变量,其特殊性表现在类型和值上。指针变量的类型是指针所指向的变量的类型,而不是自身的类型,指针变量的值是某个变量在内存中的地址,这个值将被编译器当作一个地址,而不是一个一般的数值。三、指针变量的操作及运算指针变量的操作主要有以下两个相关的运算符:1.&——取地址运算符。其中,地址运算符“&”用来表示变量的地址。其一般形式为:&变量名例如,&a表示变量a的地址,&b表示变量b的地址。变量a、b本身必须预先定义。2.*——指针运算符(或称“间接访问”运算符)。访问指针变量所指向的变量的一般格式为:*指针变量名指针变量同普通变量一样,在使用之前不仅要进行声明,而且必须赋予具体的值。未经赋值的指针变量不能使用,否则将造成系统混乱,甚至死机。指针变量的赋值只能赋予地址,决不能赋予任何其他数据,否则将引起错误。在C语言中,变量的地址是由编译系统分配的。例如:inti,j;pointer1=&i;/*将i的地址赋值给pointer1*/pointer2=&j;/*将j的地址赋值给pointer2*/【案例8-1】从键盘上输入任意矩形的长、宽,求它的周长和面积。程序代码如下:#include<stdio.h>main(){float*len,*wide,len1,wide1,m,s;/*定义了两个指针变量len,wide*/len=&len1;/*将变量len1的地址赋给len*/wide=&wide1;/*将变量wide1的地址赋给wide*/printf("\n请输入矩形的长和宽:");scanf("%f%f",&len1,&wide1);m=(*len)*(*wide);/**len表示指针len所指向的变量的值,求矩形的面积*/s=2*((*len)+(*wide));/*求矩形的周长*/printf("\n该矩形的周长是:%f",s);printf("\n该矩形的面积是:%f",m);}【说明】定义变量时,变量名前加“*”,表示该变量为指针变量。而使用指针变量时,变量名前加“*”,则表示该指针变量所指向的变量的值。如:float*len中的*len表示定义指针变量len,而语句m=(*len)*(*wide)中的*len代表指针变量len所指向的变量的值。【试一试】用指针来完成第六章中案例6-4中两个变量的值的交换。3.指针的赋值运算赋值运算是指使指针变量指向一特定的内存地址。指针变量的赋值运算只能在相同的数据类型之间进行。(1)相同类型指针可相互赋值,不同类型指针不能相互赋值。例如:charch='A',*p1=&ch;intn=10,*p2=&n;intm=100,*p3=&m;doubled=10.0,*p4=&d;p4=p1;p1=p4;p4=p2;/*错误*/p2=p1;/*正确*/(2)不能对指针赋一个常量地址。例如:inta[4]={1,2,3,4};int*p;p=2000;printf(”%x”,p);/*错误,不应把一个整数赋给指针变量*/p=a;/*正确p指向数组a的首地址*/(3)空指针P=NULL;/*表示p不指向任何数据*/4.指针的加减运算(1)指针加整数和指针减整数。可以通过对指针与一个整数进行加、减运算来移动指针,例如:p+n;p-n;等其中p是指针变量,n是整数。进行加法运算时,表示p向地址增大的方向移动;进行减法运算时,表示p向地址减小的方向移动。移动的具体长度取决于指针的数据类型,由计算机自动确定。(2)指针自增和自减运算例如:inta[4]={1,2,3,4};int*p=a,*p1=p+3;p++;p--;++p;--p指针加1或减1的结果是指针变量指向后一个数据或前一个数据的首地址,即加一个数据的字节数,或减一个数据的字节数。第二节指针与数组的关系在C语言中指针与数组的关系十分密切。因为数组中的元素是在内存中连续排列的存放的,所以任何用数组下标完成的操作都可以通过指针的移动来实现。所谓数组的指针是指数组的起始地址(首地址),数组元素的指针则是数组元素的地址。使用数组指针的主要原因是操作方便,编译后产生的代码占用空间少,执行速度快,效率高。一、指针和数组数组中所有元素都是依次存储在内存单元中的,每个元素都有相应的地址。规定数组名代表数组中第一个元素的地址。例如:inta[5];int*pi;则pi=&a[0];等价于pi=a;通过指针能引用数组元素。如:*(a+i)、*(p+i)的形式都表示数组元素a[i]。其中:a是数组名,p是指向数组的指针变量。例如:*(pi+1)=1;等价于a[1]=1;因为C规定,pi+1是下一个数组元素地址,即是元素a[1]的地址,而pi+i就是元素a[i]的地址。指向数组的指针变量实际上也可像数组变量那样使用下标,而数组变量又可像指针变量那样使用指针。【案例8-2】编写程序,要求使用指针变量完成一组成绩的输入和输出(假定有6门课程)。分析:定义指针变量p,通过p来访问score数组的元素。#defineN6#include<stdio.h>voidmain(){ intscore[N],*p; inti; printf("请输入%d门课程的成绩:",N);/*输入6门课程的成绩*/ p=score;/*将数组score的首地址赋给指针p*/ for(i=0;i<N;i++)/*用for循环语句输入6门课程的成绩*/ { scanf("%d",p); p++; } printf("%d门课程的成绩为:\n",N); for(p=score;p<score+N;p++)/*p++表示指针变量向后移动指向下一个数组元素*/ printf("%5d",*p); printf("\n");}

【注意】指针变量可以实现本身的值的改变,而数组名则不能。例如,p++是合法的,而score++是错误的。二、字符串指针字符串在内存中的起始地址称为字符串的指针,可以定义一个字符指针变量指向一个字符串。例如:charc,*p=&c;/*表示p是一个指向字符变量c的指针变量*/char*s="CProgram";/*表示s是指向字符串的指针变量,把字符串的首地址赋给了s*/

这里要注意字符指针与字符数组之间的区别。例如,有说明语句:char*str,string[]="Thisisastring.";str=string;

此时,string是字符数组,它存放了一个字符串。字符串指针str与字符数组string的区别是:str是一个变量,可以改变str使它指向不同的字符串,但不能改变str所指的字符串常量。string是一个数组,可以改变数组中保存的内容。【案例8-3】用指针作为函数的形式参数,编写字符串复制函数。

#include<stdio.h>voidstrcopy(char*str1,char*str2)/*将串str1拷贝到串str2中,函数的形式参数为指向字符的指针*/{while((*str2=*str1)!='\0')/*当str1所指的字符为结束标志'\0'时,结束循环*/{str1++;str2++;}}main(){chara[40],b[40];printf("请输入字符串:");scanf("%s",a);strcopy(a,b);/*调用函数的实际参数为一维数组名*/printf("a=%s\nb=%s\n",a,b);

}【说明】本程序中使用一维数组名作为实际参数,使用数组名作为实际参数也就是将数组的首地址传递给被调用函数。"str1++"的含义是:将指针str1的值加1。同样,"str2++"的含义是:将指针str2的值加1。程序的结束条件是:当str1所指的字符为结束标志'\0'时,结束循环。第三节指向结构体类型数据的指针一、指向结构体变量的指针一个指针变量如果用来指向一个结构体变量时,就称它为结构体指针变量,结构体指针变量中的值是所指向的结构体变量的首地址,通过结构体指针变量即可访问该结构体变量。这与数组指针的情况是相同的。结构体指针变量定义的一般形式为:struct结构体名*结构体指针变量名例如:在前面的例题中定义了student这个结构体,如要说明一个指向student的指针变量p_str,可写为:structstudent*p_stu;二、指向结构体数组的指针指针变量可以指向一个结构体数组,这时结构体变量指针的值就是整个结构体数组的首地址。【案例8-4】用指针变量输出结构体数组。#include<stdio.h>main(){structperson{intnum;charname[20];charsex;}stu[3]={{1000,"ZhangHua",'M'},{1001,"LiPing",'F'},{1002,"WangMing",'M'}};/*定义了person结构体类型的数组stu并作了初始化*/structperson*p;for(p=stu;p<stu+3;p++)printf("%4d%-20s%3c\n",p->num,p->name,p->sex);}【说明】在程序中,定义了person结构体类型的数组stu并作了初始化,在main函数中定义了指针p,指向stu数组,在循环语句中,p被赋予stu的首地址,然后循环3次,输出stu数组中各成员值。三、链表的概念链表是结构体最重要的应用,它是一种非固定长度的数据结构,是一种动态存储技术,它能够根据数据的结构特点和数量使用内存,尤其适用于数据个数可变的数据存储。链表有一个头指针变量,以head表示,它存放一个地址。该地址指向一个元素。链表中每一个元素称为结点,每个结点都应包括两个部分:一为用户需要的实际数据,二为下一个结点的地址。因此,head指向第一个元素;第一个元素指向第二个元素……直到最后一个元素,该元素不再指向其他元素,它称为表尾,它的地址部分放一个“NULL”(表示空地址),链表到此结束。(一)链表的实现1.结点定义结点包含数据域和指针域,结点结构可描述为:其中Data域用来存放结点本身信息,类型由具体问题而定,Next域存放下一个元素的地址。2.单链表的逻辑结构为了能顺次访问每个结点,需要保存单链表第一个结点的存储地址。这个地址称为单链表的头指针,用head表示。DateNext图8.1链表的逻辑结构3.在C语言中单链表结点类型可以定义为:structnode{intdata;/*数据域*/structnode*link;/*链域,是指向与结点类型完全相同的其他结点的指针*/};(二)链表的操作对链表施行的操作有以下几种:heada1a2a4a5a31.建立链表:是在确定了链表结点的结构之后给链表中的若干个结点输入数据。2.链表的输出:是将一个已经建立好的链表中各个结点的数据字段部分地或全部地输出。3.插入一个结点:是指将一个已知的结点插入到已经建立好的链表中。4.删除一个结点:是指从已经建立好的链表中按指定关键字段删除一个或多个结点。5.查找一个结点【案例8-5】建立一个带头结点的单链表,数据字段为整数,当输入的整数为0时,结束建立链表,并将链表的数据字段部分全部显示在屏幕上。#include<stdio.h>structnode/*定义链表结点的结构*/{intnum;structnode*next;/*定义了一个指向node类型的结点的指针*/};main(){structnode*head;/*定义一个头结点*/structnode*create();voidprint(structnode*head);head=create();/*创建单链表*/print(head);/*输出单链表*/}structnode*create()/*创建单链表函数返回的是与节点相同类型的指针*/{structnode*head,*p1,*p2;p1=p2=(structnode*)malloc(sizeof(structnode));/*申请新节点*/head=NULL;/*创建一个空表*/scanf("%d",&p1->num); /*输入节点的值*/p1->next=NULL; /*将新节点的指针置为空*/while(p1->num!=0) /*输入节点的数值不等于0,执行循环体*/{p1->next=NULL;/*将新结点的指针置为空*/if(head==NULL)head=p1;/*如果是空表,将结点插入表头*/elsep2->next=p1;/*如果是非空表,将结点插入到表尾*/p2=p1;/*指针p2指向链表的最后一个结点*/p1=(structnode*)malloc(sizeof(structnode)); scanf("%d",&p1->num); /*输入节点的值*/p1->next=NULL;/*申请新结点*/}p2->next=NULL;/*将最后一个结点的指针域赋为空值*/returnhead; /*返回链表的头指针*/}voidprint(structnode*head) /*输出以head为头的链表各节点的值*/{structnode*temp;temp=head; /*取得链表的头指针*/while(temp!=NULL) /*只要是非空表*/{printf("%3d",temp->num); /*输出链表节点的值*/temp=temp->next; /*指针移向下一个结点*/}}【思考】程序中的(structnode*)malloc((sizeof(structnode)))起什么作用?第四节项目任务:用动态内存分配实现输入输出管理【项目案例】用动态内存分配实现输入输出管理分析:在前面的章节中学生的数据都存储在相应的数组中,因此必须事先定义数组的长度,如果事先难以确定要处理的学生的人数,就必须把数组定义得足够大,显然,这将会浪费大量的内存空间。而链表则是根据需要开辟内存单元。下面的程序中,我们将用链表来存储学生信息,链表的每一个结点都是结构体变量,它包括学生学号、姓名、语文、数学、英语、物理、化学这5门课的成绩。程序如下:#defineN4#include<stdio.h>structscore/*定义链表结点的结构*/{charxuehao[10];charmingzi[10];intscor[5];structscore*next;/*定义了一个指向score类型的结点的指针*/};main(){structscore*head;structscore*data_input();voidprint(structscore*head);head=data_input();print(head);}structscore*data_input()/*---------学生数据输入---------*/{inti,j;structscore*head,*p1,*p2;/*其中定义了一个头指针head,指针p1指向新插入的结点,指针p2指向链表的最后一个结点*/printf("输入每个学生的学号、姓名及每门课程的成绩,以回车结束\n");p1=p2=(structscore*)malloc(sizeof(structscore));/*申请新结点*/head=NUL

温馨提示

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

评论

0/150

提交评论