C语言程序设计实例教程(慕课版)(第3版)课件 常中华 第8、9章 指针、函数_第1页
C语言程序设计实例教程(慕课版)(第3版)课件 常中华 第8、9章 指针、函数_第2页
C语言程序设计实例教程(慕课版)(第3版)课件 常中华 第8、9章 指针、函数_第3页
C语言程序设计实例教程(慕课版)(第3版)课件 常中华 第8、9章 指针、函数_第4页
C语言程序设计实例教程(慕课版)(第3版)课件 常中华 第8、9章 指针、函数_第5页
已阅读5页,还剩103页未读 继续免费阅读

下载本文档

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

文档简介

第8单元

指针摘要8.1地址和指针8.2指针变量8.3指向数组的指针8.3.1指向一维数组的指针8.3.2指向字符串的指针8.3.3指向二维数组的指针8.4用指针变量作为函数参数8.5返回指针值的函数实例分析与实现8.1地址和指针地址和指针…43内存用户数据区ij0x62fe4425k0x62fe48main(){...inti=3;intj=4;intk=25;...}…0x62fe4c地址和指针i3把某变量的地址称为“指向该变量的指针”取地址运算符“&”&iprintf("%x",&i);结果:62fe440x62fe448.2指针变量指针变量指针变量:用来存放变量的地址(变量的指针)的变量。定义形式为

数据类型名

*指针变量名;其中,星号“*”为定义指针变量的标志,称为指针运算符。i30x62fe440x62fe44pointer_i存放地址(指针)的变量存放数值的变量0x62fe65指针变量例如:int

i=3;/*定义整型变量i*/int*pointer_i;/*定义指针变量pointer_i*/pointer_i=&i;

/*变量i的地址存放在指针变量pointer_i中*/以上两句可以合并成:int*pointer_i=&i;星号“*”也称为取值运算符,*pointer_i表示变量i的值。指针变量【例8.1】通过指针变量访问整型变量。#include<stdio.h>main(){ inta,b,*p; a=5; p=&a; b=*p+5; printf("a=%d,*p=%d,b=%d\n",a,*p,b);}8.2指针变量【例8.2】取地址符号与取值符号是互为逆运算的。#include<stdio.h>main(){ inta=5,*p; p=&a; printf("%d,%d,%d\n",&a,p,&(*p)); printf("%d,%d,%d\n",a,*p,*(&a));}运行程序,结果为:&a、p、&(*p)取值是相同的,表示指针变量p存放的变量a的地址。a、*p、*(&a)取值是相同的,表示指针p所指向的变量a的值。指针变量两点说明:&和*为单目运算符,优先级别仅次于括号和成员运算符,具有右结合性。运算符“&”的操作数允许是一般变量,运算符“*”的操作数必须为指针变量或地址型表达式。

指针变量【例8.3】利用指针对两个数进行排序#include<stdio.h>main(){ int*p1,*p2,*p,a,b; printf("请输入两个整数\n"); scanf("%d%d",&a,&b); p1=&a; p2=&b; if(a<b)

{p=p1;p1=p2;p2=p;} printf("a=%d,b=%d\n",a,b); printf("max=%d,min=%d\n",*p1,*p2);}45&a&b7845&a&b78ap1p2bap1p2b8.3指向数组的指针8.3.1指向一维数组的指针一维数组的名字#include<stdio.h>main(){ inta[5]={1,2,3,4,5}; printf("%d,%d\n",a,&a[0]); printf("%d,%d\n",a[0],*a);}在C语言中,数组名代表数组的起始地址,这是一个地址常量,不允许赋值。数组名是指向数组第一个元素的指针。指向一维数组的指针变量1.指向一维数组元素的指针变量定义例如:inta[10];int*p;p=&a[0];或:inta[10];int*p=a;或:inta[10];int*p=&a[0];指向一维数组的指针如果有定义:inta=3,array[5]={1,2,3,4,5},*p1,*p2;那么如下赋值运算都是正确的: p1=&a;

//将变量a的地址赋值给p1 p1=array;

//将数组array的首地址赋值给p1 p1=&array[2];//将数组元素array[2]的地址赋值给p1 p2=p1;

//将指针变量p1的地址赋值给p2数组指针的运算如果p指向数组a的首地址,即a[0]的地址,那么表达式p+1、p+2…的含义是什么?数组指针的运算pp+1p+2#include<stdio.h>main(){ inta[3]; int*p; p=a; printf("%x,%x,%x",p,p+1,p+2);}0x62fe2f0x62fe30a[0]0x62fe310x62fe320x62fe330x62fe34a[1]0x62fe350x62fe360x62fe370x62fe38a[2]0x62fe390x62fe3a0x62fe3b0x62fe3c*p==a[0]*(p+1)==a[1]*(p+i)==a[i]数组指针的运算对于指针变量p,可以做以下运算:

p++,p--,p+i,p-i,p+=i,p-=i等。#include<stdio.h>main(){ inta[]={1,3,6,7,9,12}; intx,*p=&a[2]; x=(*--p)++; printf("x=%d\n",x); printf("a[1]=%d\n",a[1]);}【例8.5】显示指针变量的当前值#include<stdio.h>main(){ inta[5],*p,i; p=a; for(i=0;i<5;i++) scanf("%d",p++); printf("\n"); for(i=0;i<5;i++,p++) printf("%d\t",*p);}数组指针的运算p0x62fe2f0x62fe303a[0]0x62fe348a[1]0x62fe387a[2]0x62fe3c5a[3]0x62fe401a[4]0x62fe4400x62fe48104853200x62fe4c00x62fe5058376640x62fe5400x62fe58pp=a;指针也可以进行关系运算:①若p1和p2指向同一个数组,则p1<p2表示p1指的元素在前;p1>p2表示p1指的元素在后;p1==p2表示p1与p2指向同一个元素。②若p1和p2不指向同一个数组,比较毫无意义。

a数组a[0]a[i]a[n]p1p2数组指针的运算p1数组指针的运算③若p1与p2指向同一数组,则p1-p2的值是两个指针之间的元素个数。④p1+p2无意义。a数组a[0]a[1]a[4]p2p18.3指向数组的指针8.3.2指向字符串的指针指向字符串的指针在C语言中,字符串在内存中的存放方式与字符数组存储方式是一致的。计算机给字符串自动分配一个首地址,并在字符串尾部添加字符串结束标志’\0’。可以使用指向字符数组的指针来灵活方便地进行字符串的处理。

指向字符串的指针main(){ charch[10]="Hello"; char*p; p="China"; printf("%s\n",p); p++; printf("%s\n",p); printf("%c\n",*p); p=ch; printf("%s\n",p); return0;}用字符串常量为指针变量赋值,是将字符串的首地址赋给指针变量不能写成:charch[10];ch=“hello”;因为数组名是常量,不能被赋值。指向字符串的指针【例8.8】使用字符型指针复制字符串#include<stdio.h>main(){ charstr1[10],str2[10],*p,*q; p=str1; q=str2; gets(str1); while(*p){*q=*p;

p++;q++;} *q='\0'; puts(str1); puts(str2);}

?是否可以改成:while(*str1)

{*str2=*str1;

str1++;str2++;}8.3指向数组的指针8.3.3指向二维数组的指针二维数组的地址#include<stdio.h>main(){ inta[3][4]={{1,2,3,4},{5,6,

7,8},{9,10,11,12}}; printf("%x,%x,%x",a,a+1,a+2);}a[0][0]a[0][1]a[0][2]a[0][3]a[1][0]a[1][1]a[1][2]a[1][3]a[2][0]a[2][1]a[2][1]a[2][3]0x62fe1ca0x62fe201a[0][0]0x62fe242a[0][1]0x62fe283a[0][2]0x62fe2c4a[0][3]a+10x62fe305a[1][0]0x62fe346a[1][1]0x62fe387a[1][2]0x62fe3c8a[1][3]a+20x62fe409a[2][0]0x62fe4410a[2][1]0x62fe4811a[2][1]0x62fe4c12a[2][3]0x62fe50a是行指针a[0]a[1]a[2]二维数组的地址#include<stdio.h>main(){ inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; printf("%x,%x,%x\n",a,a+1,a+2); printf("%x,%x,%x\n",*a,*(a+1),*(a+2)); printf("%x,%x,%x\n",*a+1,*(a+1)+1,*(a+2)+1); printf("%x,%x,%x\n",*a+2,*(a+1)+2,*(a+2)+2); printf("%d,%d,%d\n",*(*a+2),*(*(a+1)+2),*(*(a+2)+2)); printf("%d,%d,%d\n",a[0][2],a[1][2],a[2][2]);}运行结果:0x62fe1ca0x62fe201a[0][0]0x62fe242a[0][1]0x62fe283a[0][2]0x62fe2c4a[0][3]a+10x62fe305a[1][0]0x62fe346a[1][1]0x62fe387a[1][2]0x62fe3c8a[1][3]a+20x62fe409a[2][0]0x62fe4410a[2][1]0x62fe4811a[2][2]0x62fe4c12a[2][3]0x62fe50a是行指针*a是列指针*(a+i)+j=a[i]+j*(*(a+i)+j)=*(a[i]+j)=a[i][j]二维数组的地址二维数组名是数组的首地址,二维数组名的基类型不是数组元素类型,而是一维数组类型,因此,二维数组名a是一个行指针。如果有inta[3][4];则1、a、a+1、a+2...a+i是行指针。2、a[0]、a[1]、a[2]...a[i]是列指针、是一维数组名、是地址常量。3、&a[i][j]=a[i]+j=*(a+i)+j都可以表示数组元素a[i][j]的地址。4、a[i][j]=*(a[i]+j)=*(*(a+i)+j)都可以表示数组元素a[i][j]。二维数组的地址【例8.9】使用指针变量输出二维数组元素#include<stdio.h>main(){ inta[2][3]={{1,2,3},{4,5,6}},*p; for(p=a[0];p<a[0]+6;p++)

{ if((p-a[0])%3==0)printf("\n"); printf("%2d",*p);

}

}指向二维数组的指针用来存放二维数组行地址的指针变量称为指向二维数组的指针变量。定义形式:数据类型名(*指针变量名)[数组长度];例如:int(*p)[3];指向二维数组的指针【例8.10】用行指针输出二维数组元素。#include<stdio.h>main(){inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; int(*p)[4],i; for(p=a;p<a+3;p++)

{ for(i=0;i<4;i++) printf("%3d",*(*p+i));

printf("\n");}}指向二维数组的指针【例8.11】二维数组元素的不同表示方法#include<stdio.h>main(){ inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int(*p)[4],i=2,j=1; p=a; printf("%d,%d,%d",a[i][j],*(a[i]+j),*(*(p+i)+j));}如果定义了p是指向二维数组a的行指针,则可以用如下方式来表示二维数组元素a[i][j]:*(*(p+i)+j)*(p[i]+j)*(*(p+i)+j)*(*(a+i)+j)*(a[i]+j)*(*(a+i)+j)指针数组数组中的元素都是指针变量,这样的数组称为指针数组。一维指针数组的定义形式为:

类型名

*数组名[常量表达式];例如:

int*p[5];

数组p是一个包含5个元素的一维数组。它的每个元素都是基类型为int的指针变量,所以称数组p为指针数组。不要写成

int(*p)[5];指针数组指针数组常用来处理字符串Computer

network\0Visual

C++\0

Data

structure\0

Operating

system\0Java\0

例如:图书管理中的书名存储。指针数组例如:char*book[5];book[0]book[1]book[2]book[3]book[4]ComputernetworkVisualC++JavaOperatingsystemDatastructure指针数组【例8.14】将多个字符串按字母顺序(由小到大)输出。比如,排序前:"Computernetwork“"VisualC++“"Datasturcture“"Operatingsystem“"Java"排序后:"Computernetwork“"Datasturcture“"Java""Operatingsystem“"VisualC++“指针数组book[0]book[1]book[2]book[3]book[4]ComputernetworkVisualC++JavaOperatingsystemDatastructurebook[0]book[1]book[2]book[3]book[4]ComputernetworkVisualC++JavaOperatingsystemDatastructure例如:char*book[5];8.4用指针变量作为函数参数8.4用指针变量作为函数参数指针变量作为形参时,对应的实参必须为地址类型的表达式的值除了用return返回一个值之外,还可以通过指针参数返回多个数据。【例8.13】指针变量做形参,交换函数实参变量的值。#include<stdio.h>voidswap(int*p,int*q){ intt; t=*p; *p=*q; *q=t;}main(){ inta=3,b=5; printf("a=%d,b=%d\n",a,b); swap(&a,&b); printf("a=%d,b=%d\n",a,b);}8.4用指针变量作为函数参数【例】指针变量做形参,数组名做实参#include<stdio.h>voidprint(int*p,intn){ for(inti=0;i<n;i++) printf("%3d",*p++);}intmain(){ inta[5]={1,2,3,4,5}; print(a,5); return0; }8.4用指针变量作为函数参数【例】数组名做形参,数组名做实参#include<stdio.h>voidprint(intb[],intn){ for(inti=0;i<n;i++) printf("%3d",*b++);}intmain(){ inta[5]={1,2,3,4,5}; print(a,5); return0; }8.5返回指针值的函数8.5返回指针的函数函数可以返回指针型的数据。这种返回指针值的函数,一般定义形式为:类型名*函数名(参数列表)

例如:

int*a(intx,inty);8.5返回指针的函数【例8.16】从键盘输入一个字符串,再输入一个要查找的字符,编写match函数在字符串中查找该字符。若找到相同字符,则返回一个指向该字符所在位置的指针;如果没有找到,则返回一个空(NULL)指针。8.5返回指针的函数#include<stdio.h>intmain(){char*match(char,char*);

chars[50],c,*p;printf("请输入一个字符串:\n");gets(s);

printf("请输入一个字符:\n");

c=getchar();

p=match(c,s);

printf("查找结果:");

if(*p==‘\0’)

printf("字符串中无此字符\n");

else printf("%c查找成功!\n",*p);}char*match(charc,char*s){

inti=0; while(s[i]!='\0'&&c!=s[i]) i++; return&s[i];}例如:输入Chinaisacountrywithalonghistory.例如输入‘h’8.6指向函数的指针

指向指针的指针8.6.1指向函数的指针

将函数名赋予一个指针变量,使指针变量指向函数所在的内存区域,这种指针就是指向函数的指针。

指向函数的指针变量的一般定义形式为

数据类型

(*指针变量)(参数列表)指令1指令2指令3......func()8.6.1指向函数的指针例如:intfunc(intx);/*声明一个函数*/int(*p)(intx);/*声明一个指向函数的指针变量*/p=func;/*将func函数的首地址赋给指针变量p*/或者使用下面的方法将函数地址赋给函数指针:p=&func;8.6.1指向函数的指针例8.17使用指向函数的指针变量调用一个函数,求出两个整数的和。#include<stdio.h>intsum(){

inta,b;

printf("请输入两个整数:\n");

scanf("%d,%d",&a,&b);

returna+b;}intmain(){intresult;int(*p)(); //p是指向无参函数的指针变量p=sum;//变量p指向sum函数的首地址

result=(*p)(); //通过p指针调用sum函数

printf("两个数的和是%d\n",result);}8.6.1指向函数的指针关于该例题的几点说明:(1)int(*p)()不能省略括号(2)p=sum;给指针变量赋值时,必须给出函数名,不必给出参数。(3)p指向sum函数,但不能使用p++、p--、p+n等运算指向函数内某条指令。(4)因为类型声明语句int(*p)(),所以p只能指向类型一致的函数,不能指向有参函数。8.6.1指向函数的指针【例8.18】使用指向函数的指针变量调用有参函数,求两个整数中较大的数。#include<stdio.h>intmax(inta,intb){ returna>b?a:b;}intmain(){ inta,b,result; int(*p)(int,int); p=max; //给p赋值时不必给出函数参数printf("请输入两个整数:\n");scanf("%d,%d",&a,&b); result=(*p)(a,b); //通过p指针调用max函数printf("其中较大的数是%d\n",result); }8.6.2指向指针的指针

如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。

例如:inta=100;int*p1=&a;int**p2=&p1;实例分析与实现实例1

计算字符串中子串出现的次数。例如:“China”在“Chinaisagreatcountry.Chinaisacivilizedcountry.”中出现的次数为2。实例2有一个3X3的矩阵a,编程对它进行转置操作,用指向二维数组的指针完成。【想一想】(1)怎样动态申请和释放一段内存?(2)chara[]和char*a何时相同,何时不同?Thechapterisover!单元9结构体和共用体摘要9.1结构体类型9.1.1结构体类型的定义9.1.2结构体变量的定义9.1.3结构体变量的初始化9.1.4结构体变量的引用9.2结构体数组9.3结构体指针9.4结构体与函数9.5链表9.5.1链表的概念9.5.2链表的实现9.5.3链表的操作9.6构造数据类型-共用体9.6.1共用体类型的定义9.6.2共用体变量的定义9.6.3共用体变量的初始化和引用9.7枚举类型9.7.1枚举类型的定义9.7.2枚举变量的定义9.8使用typedef声明新类型实例分析与实现9.1结构体类型9.1.1结构体类型的定义学生:学号 intsno;姓名 charname[20];班级 charclassname[20];课程成绩

intgrade[3];将多个不同类型的数据组成一个“整体”9.1.1结构体类型的定义struct结构体类型名称{

数据类型成员名1;

数据类型成员名2; …

数据类型成员名n;};【例】structstudent{ intsno; //学号 charname[20]; //姓名 charclassname[20];//班级

intgrade[3];//成绩};9.1结构体类型9.1.2结构体变量的定义9.1.2结构体变量的定义结构体类型变量:structstudent{ intsno; charname[20]; charclassname[20]; intgrade[3];};structstudentstu1;基本数据类型变量 int x; float y; char c;数据类型名变量名数据类型名变量名9.1.2结构体变量的定义2.在定义结构体类型的同时定义结构体变量structstudent{ intsno; charname[20]; charclassname[20]; intgrade[3];};stu1;1.先定义结构体类型,再定义结构体变量structstudent{ intsno;

charname[20]; charclassname[20]; intgrade[3];};structstudentstu1;3.直接定义结构体变量struct{ intsno; charname[20];

charclassname[20]; intgrade[3];};stu1;9.1.2结构体变量的定义structstudent{ intsno; charname[20]; charclassname[20]; intgrade[3];};stu1;

结构体变量定义后,编译器就会为其分配内存。它所占用的实际字节数,就是其各个“成员”所占用字节数的总和。本例中变量stu1中各成员所占用内存:9.1结构体类型9.1.3结构体变量的初始化9.1.3结构体变量的初始化在定义结构体类型和结构体变量的同时,对结构体变量初始化。【例】structstudent{ intsno; charname[20]; charclassname[20]; intgrade[3]}stu1{201601,"李磊","软件16级1班",{90,85,80}};9.1结构体类型9.1.4结构体变量的引用9.1.4结构体变量的引用如何引用结构体变量中的一个成员,其语法结构是:结构体变量名.成员名【例】stu1.sno=20160101;="李磊";stu1.classname="软件16级1班";stu1.grade[0]=90;stu1.grade[1]=85;stu1.grade[2]=80;“.”是一个运算符,表示对结构体变量的成员进行访问9.1.4结构体变量的引用【例】要求通过键盘输入一个学生信息,并且输出这个学生的所有信息。

分析:1、定义结构体类型student描述学生信息。其成员包括学号、姓名、班级、三门课程成绩。

2、通过键盘输入每个成员的数据。9.1.4结构体变量的引用【例】从键盘输入学生信息,计算该学生的成绩平均分,并输出该学生的信息。

分析:1、定义结构体类型student描述学生信息。

2、学生的成绩是该结构体类型中的一个成员,使用数组类型存储数据,通过遍历数组中的数组元素,经过累加求和,就可以计算成绩的平均分。9.2结构体数组structstudent{ intsno; charname[20];

charclassname[20]; doublegrade[3]};structstudentstu;如何描述30个学生信息?9.2结构体数组【例】structstudent{ intsno; charname[20]; charclassname[20]; doublegrade[3]};structstudentstu[30];9.2结构体数组【例】要求通过键盘输入两个学生信息,并且输出这两个学生的所有信息。分析:1、定义一个包含2个元素的数组,数组元素的类型是结构体类型。2、使用循环结构遍历访问数组元素。9.3结构体指针9.3结构体指针【例】structstudent{ intsno; charname[20]; charclassname[20]; doublegrade[3]};structstudents

={201601,"李磊","软件16级1班",{90,85,80}};;structstudent*p;p=&s;9.3结构体指针*(结构体指针变量).成员名(*p).sno=20160101;(*p).name="李磊";(*p).grade[0]=90;p->.sno=20160101;p->.name="李磊";p->.grade[0]=90;结构体指针变量->.成员名【例】要求通过键盘输入一个学生信息,并且输出这个学生的所有信息。(用指针变量引用结构体变量的成员。)9.3结构体指针9.4结构体与函数结构体与函数的关系主要分为三种:结构体变量作为函数参数结构体指针作为函数参数函数的返回值是结构体类型。9.4结构体与函数

#include<stdio.h>structstudent{ intsno; charname[20];

doublegrade[3];};voidshowStuInfo(structstudentstu){ printf("学号:%d\n",stu.sno); printf("姓名:%s\n",); printf("课程1:%lf\n",stu.grade[0]); printf("课程2:%lf\n",stu.grade[1]); printf("课程3:%lf\n",stu.grade[2]);}intmain(){ structstudentstu1={20160101,"李磊",{90,85,80}};

showStuInfo(stu1); return0;}值传递#include<stdio.h>structstudent{ intsno; charname[20]; doublegrade[3];};voidshowStuInfo(structstudent*p)//结构体指针作函数参数{ printf("学号:%d\n",(*p).sno); printf("姓名:%s\n",(*p).name);

printf("课程1:%lf\n",(*p).grade[0]); printf("课程2:%lf\n",(*p).grade[1]); printf("课程3:%lf\n",(*p).grade[2]);}intmain(){ structstudentstu1={20160101,"李磊",{90,85,80}};

showStuInfo(&stu1); return0;}地址传递函数的参数是结构体变量

值传递函数的参数是结构体指针

地址传递9.5链表头指针:存储的是第一个结点的首地址。结点:链表中的每一个元素。

数据域:存储用户需要的数据

指针域:存储下一个结点的地址9.5.1链表的概念链表

在执行过程中根据需要动态申请空间,结点的个数可以根据需要增加或减少。

占用不连续的存储空间。数组

定义时必须指定数组长度,所占用的内存空间大小固定。

占用连续的存储空间。9.5.1链表的概念结点的定义structnode{ intdata;

structnode*next;};9.5.2链表的实现1、初始化链表2、在尾结点后插入新结点9.5.2链表的实现structnode*h,*s;h=NULL;//建立“空”链表s=(structnode*)malloc(sizeof(structnode));//建立首结点s->next=NULL;h=s; 1、初始化链表9.5.2链表的实现p=(structnode*)malloc(sizeof(structnode));p->data=1;

p->next=NULL;s->next=p;s=p;2、在尾结点后插入新结点9.5.2链表的实现p=h->next;//指针指向第一个结点while(p!=NULL){

printf("%d\n",p->data);

p=p->next;//指针指向下一个结点}

9.5.3链表的操作9.6共用体类型9.6.1共用体类型的定义union结构体类型名称{

数据类

温馨提示

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

评论

0/150

提交评论