第五章 指针与结构.docx

上传人:b****3 文档编号:3540976 上传时间:2022-11-23 格式:DOCX 页数:31 大小:111.14KB
下载 相关 举报
第五章 指针与结构.docx_第1页
第1页 / 共31页
第五章 指针与结构.docx_第2页
第2页 / 共31页
第五章 指针与结构.docx_第3页
第3页 / 共31页
第五章 指针与结构.docx_第4页
第4页 / 共31页
第五章 指针与结构.docx_第5页
第5页 / 共31页
点击查看更多>>
下载资源
资源描述

第五章 指针与结构.docx

《第五章 指针与结构.docx》由会员分享,可在线阅读,更多相关《第五章 指针与结构.docx(31页珍藏版)》请在冰豆网上搜索。

第五章 指针与结构.docx

第五章指针与结构

第5章指针与结构

主要内容:

指针概念与指针的简单运用;指针与数组(一维、二维数组),指向字符串的指针;结构的定义与引用,结构数组的运用。

教学要求:

⏹掌握指针与数组间关系

⏹能用指针编写程序

⏹掌握结构体的使用

⏹能将指针与结构体混和使用,解决生活中的实际问题

5.1指针与指针的引用

我们知道变量在计算机内是占有一块存贮区域的,变量的值就存放在这块区域之中,在计算机内部,通过访问或修改这块区域的内容来访问或修改相应的变量。

C语言中,对于变量的访问形式之一,就是先求出变量的地址,然后再通过地址对它进行访问,这也就是在这里所要论述的指针与指针变量。

指针在C语言中运用非常广泛,原因之一是较之其他方法,指针能设计出更加高效、灵活、简洁的代码,就好比一个学生的学号和姓名,它们都可以代表某一个学生,但是学号更通用,更规范.可以说指针是C语言中的精华。

5.1.1指针的概念

要弄清楚什么是指针,必须先弄清楚数据在内存中的存储方式。

程序在执行过程中所需要处理的各种数据,都被存放在内存中。

编译系统会根据程序中定义的变量的类型,分配一定长度的空间。

例如,一般为整型变量分配两个字节,为字符型分配一个字节。

为了方便管理,内存空间被划分成了若干个大小相同(1个字节)的存储单元,并且为每个存储单元安排了一个编号,这个编号被称为内存地址,它相当于旅馆的房间号,而在地址所对应的内存单元中存放数据,这就相当于在旅馆房间号所对应的房间中居住旅客一样。

请务必要弄清楚内存单元的地址和内存单元的内容这两个概念的区别。

假设程序定义了两个整型变量i,编译时系统分配2000和2001两个字节给变量i,2002和2003字节给变量j。

并且将i赋值为5,j赋值为6。

如图5-1所示,这时的表述是:

变量i的值是5,变量i的地址是2000;变量j值是6,变量j的地址是2002;

地址编号

2000

5

2001

2002

6

2003

2004

图5-1直接存取

在程序中一般是通过变量名来对内存单元进行存取操作的。

其实程序经过编译以后已经将变量名转换为变量的地址,对变量值的存取都是通过地址进行的。

例如有语句:

j=i;

它是这样执行的:

直接根据变量名与地址的对应关系(这个关系是编译时确定的),找到变量i的地址2000,然后从由2000开始的两个字节中取出数据(即变量的值5),然后把5直接存入变量j的地址为2002开始的两个内存单元中,这种按变量地址存取变量值的方式称为“直接访问”方式。

在C语言中,还可以采用另一种称之为“间接访问”的方式,将变量i的地址存放在另一个变量中,在C语言中规定,可以在程序中定义一种特殊变量叫指针变量,专门用于存放地址。

假设现定义了一个指针变量p,它被分配为3000和3001两个字节,同时定义了一个整型变量i,且变量i被分配的为2000和2001两个字节。

可以通过下面语句将i的地址存放到p中:

p=&i;

这时变量p的值就是2000,也就是变量i所占用的单元的起始地址。

如果采取“间接存取”方式,要存取变量i的值,就必须先找到存放“i的地址”的变量p,从中取出i的地址(2000),然后到2000、2001字节取出i的值5,见图5-2:

图5-2间接存取

由于通过地址能找到所需要的变量单元,我们可以说,地址指向该变量单元,因此,在C语言中,将地址形象化为指针。

换句话说,一个变量的地址称之为该变量的“指针”,而指针变量是在一种专门存放其他变量在内存中的地址的特殊变量。

指针变量的值是地址。

5.1.2指针变量的定义

C语言规定所有变量必须遵循先定义后使用的规则。

指针变量定义格式是:

数据类型*指针变量名;

例如:

int*p;/*p是指向int型变量的指针变量*/

float*q;/*q是指向float型变量的指针变量*/

char*a,b;/*其中a是指向char型变量的指针变量,而b是字符型一般变量*/

那么,怎样使一个指针变量指向另外一个变量呢?

可以用赋值语句使一个指针变量得到另外一个变量得地址,从而使它指向该变量。

例如:

inti;/*定义i为整型变量*/

int*p;/*定义p为指针变量*/

p=&i;/*将变量i的地址赋值给指针变量p*/

注意在定义指针变量时,要注意两点:

✧指针变量前面的“*”表示该变量的类型为指针变量。

例如:

int*p;指针变量名是p,而不是*p,这是与定义整型或者字符型变量形式不同的。

✧在定义指针变量时必须指定基类型。

并且指针变量的类型是它指向的内存单元中存放数据的类型,而不是指针变量的值的类型。

那么既然指针变量,是用来存放地址的,为什么还要指定它的类型呢?

要知道不同数据在内存中所占的字节数是不同的(例如:

整型占2个字节,字符型占1个字节),在本章的稍后将要介绍指针的移动和指针的运算,例如“使指针移动1个位置”,那么这个1代表什么呢?

如果一个指针指向的是一个整型数据,那么“使指针移动1个位置”意味着移动2个字节,如果一个指针指向的是一个字符型数据,那么“使指针移动1个位置”意味着移动1个字节。

因此必须指定指针变量的基类型,并且一个指针变量只能指向同一类型的变量。

例如以下赋值是错误的:

floata;/*定义a为float型变量*/

int*p;/*定义p为基类型为int的指针变量*/

p=&a;

/*错误!

将float型变量的地址存放到指向整型变量的指针变量中,*/

【例5-1】通过指针变量访问整型变量

main()

{

inta,b;/*定义a,b为整型变量*/

int*p1,*p2;/*定义p1,p2为指向整型变量的指针变量*/

a=100;/*为变量a赋值为100*/

b=10;/*为变量b赋值为10*/

p1=&a;/*将a的地址赋给p1*/

p2=&b;/*将b的地址赋给p2*/

printf("%d,%d\n",a,b);/*输出a,b的值*/

printf("%d,%d\n",*p1,*p2);/*输出p1,p2所指向的变量的值*/

}

运行结果为:

100,10

100,10

5.1.3指针变量的引用

1.深入理解两个运算符:

*与&

设有定义语句:

int*p,a;

✧“*”是指针运算符。

“*p”出现在定义语句和非定义语句中的含义是不一样的!

在定义语句中,星号*声明其后的变量p为一指针(地址)变量;在非定义语句中“*p”表示指针变量p指向的地址单元内的值。

✧“&”是地址运算符,“&a”表示取某一普通变量a的地址。

很显然,*(&a)与a相当。

✧p=&a;表示将a的地址赋给了指针变量p,即p与&a指向了同一地址单元!

注意

✧“p=&a;”语句形式经常用到,使用时指针变量与一般变量的类型必须一致!

例如本例中p,a均为整型变量,如果p,a的类型不一致那么就是错误的!

“p=a;”是非法的语句,=号两边变量意义不同,左边为指针变量,右边为普通变量。

✧C规定,不能直接将一个常数赋给指针变量(除0以外,代表空指针)。

【例5-2】输入a、b两个整数,使用指针变量按大小顺序输出这两个整数。

main()

{

inta,b,*p1,*p2,*p;

p1=&a;

p2=&b;

scanf(″%d,%d″,p1,p2);/*等价于语句scanf(″%d,%d″,&a,&b);*/

if(*p1<*p2)/*判断a,b整数的大小*/

{

p=p1;

p1=p2;

p2=p;

}

printf(″a=%d,b=%d\n″,a,b);

printf(″max=%d,min=%d\n″,*p1,*p2);

}

运行情况如下:

输入:

6,8

输出:

6,8

max=8,min=6

当输入6,8后,由于*p1<*p2,将p1和p2交换。

交换前的情况如图5-3(a),交换后

的情况如图5-3(b)。

请注意,a和b的值并没有发生交换,它们仍然保持原值,但是p1和p2的值改变了。

p1的原值为&a,后来变成了&b,p2的原值为&b,后来变成了&a。

这样在输出*p1,*p2时,实际上时输出变量b和a的值,因此输出的结果为8,6。

该问题的解决思想是目标变量值不变,改变指针变量的指向求解。

请同学们想一想:

如何实现利用指针变量直接改变目标变量的值求解?

2.指针变量的算术、增量、关系等运算

设有定义语句:

int*p,*p1,*p2,a,n,v;

intq[10];

则:

(1)指针赋值运算规定,可将一个变量的地址赋给同类型的指针变量,但不能直接将常数赋给指针变量。

如“p=&a;”合法,“p=5000;”非法。

(2)指针的加减运算

✧只有当指针变量指向数组时指针的加减运算才有意义。

✧指针的加减运算是以基类型为单位(即sizeof(类型))的。

✧p+n 表示p+n*sizeof(指针类型),即从p算起,后边第n个数的地址。

✧p-n 表示p-n*sizeof(指针类型),即从p算起,前边第n个数的地址。

✧指针变量可加减一个整型表达式。

如:

p1++、p2+3、p2-2。

p++,p--,++p,--p 结果是指向下一个(或上一个)数据的地址,而不是指向下一个(或上一个)地址单元。

✧两个指针变量不能作加法运算,只有当两个指针变量指向同一数组时,进行指针变量相减才有实际意义。

如:

当指针变量p2和p1同时指向数组q时,p2-p1结果表示两个地址之间能够存放某种类型数据的个数,当然数据类型与指针的类型须一致。

(3)当*与++、--结合时应注意其优先顺序和结合性:

三个运算符优先级相同,但结合顺序是从右向左。

 v=*p++:

等价v=*(p++),先取p指向单元值赋给普通变量v,然后p自增1指向下一数据单元。

 v=*++p:

表示p先自增指向下一数据单元,再将该单元之值赋给普通变量v。

 v=(*p)++:

将(*p)值先赋给v,然后(*p)内容再增1。

 v=++(*p):

将(*p)内容增1后赋给v。

(4)指针关系运算:

✧指向同一数组的两个指针可以进行关系运算,表明它们所指向元素的相互位置关系。

如:

p2>p1、p2==p1。

✧指针与一个整型数据进行比较是没有意义的。

✧不同类型指针变量之间比较是非法的。

✧零可以与任何类型指针进行==、!

=的关系运算,用于判断指针是否为空指针。

例如:

p1==0;作用是判断指针变量p1是否为空.

 

5.2指针与数组

从前面知道,一个数组包含有若干元素,数组各元素都在内存中占用存储单元,它们都有相应的地址,而这些存储单元是连续的区域,数组名代表这块空间的起始地址。

而指针变量既然可以指向地址,当然也可以指向数组元素的地址。

通常人们习惯将数组的首地址存放到一个指针变量中,然后通过指针加减运算,存取数组各元素。

如定义语句

intx[]={1,2,3,4,5},*p=x;

此语句中的“*p=x”,将x数组的首地址赋给了指针变量p,显然可以在定义语句中将它替换成“*p=&x[0]”形式。

用指针来指向数组元素,比用纯数组的方式操作数组要方便得多,因为数组名不能运算,而指针是可以运算的。

使用指针能使目标程序占内存少,运行速度快。

5.2.1指向一维数组的指针

定义一个指向数组元素的指针的方法,与以前介绍的指向变量的指针变量定义方法相同。

例如:

inta[10];/*定义a为包含10个整型数据的数组*/

int*p;/*定义p为指向整型变量的指针变量*/

p=&a[0];/*把元素a[0]的地址赋给指针变量p*/

在C语言中规定数组名(不包含形参数组名,形参数组并不占有实际的内存单元)代表数组中首地址(即数组中第一个元素的地址)。

因此,下面两个语句等价:

p=&a[0];

p=a;

注意指针变量指向数组并不是指向整个数组,而是指向了数组中第一个元素。

上述“p=a;”的作用是:

“把数组的首地址赋值给p”,而不是“把数组a各元素的值赋值给p”。

那么要通过指针引用数组元素,应该如何实现呢?

按C语言规定:

如果指针变量p已指向数组中的一个元素,那么p+1指向同一数组中的下一个元素,而不是简单的将p的值(地址)简单的加1。

例如,数组元素是float型,每个元素占4个字节,那么p+1意味着使p的值(地址)加4个字节,以使它指向下一个元素;又例如数组元素是int型,每个元素占2个字节,那么p+1意味着使p的值(地址)加2个字节,以使它指向下一个元素。

也就是说,p+1所代表的地址实际上使p+1×d,d代表一个数组元素所占的字节数。

根据上述,引用一个数组元素可以用两种方法:

(1)下标法,如a[0]形式;

(2)指针法,如*(a+i)或者*(p+i)。

其中a是数组名,p是指向数组元素的指针变量,初值为数组a的首地址。

例如有语句如下:

inta[10],*p;

⑴数组名是该数组的指针

✧a是数组的首地址(即a[0]的地址),是一个指针常量。

a=&a[0],a+1=&a[1],…,a+9=&a[9]

✧数组元素的下标表示法:

a[0],a[1],…,a[i],…,a[9]

✧数组元素的指针表示法:

*(a+0),*(a+1),…,*(a+i),…,*(a+9)

⑵指向一维数组元素的指针变量

由于数组元素也是一个内存变量,所以此类指针变量的定义和使用与指向变量的指针变量相同。

例如有语句如下:

inta[10],*p;

p=a;/*相当于p=&a[0];*/

此时p指向a[0],下面用p表示数组元素

✧下标表示法:

p[0],p[1],…,p[i],…,p[9]

✧指针表示法:

*(p+0),*(p+1),…,*(p+i),…,*(p+9)

提醒

用指针变量引用数组元素,必须关注其当前值。

如果指针变量p的初始值不一样,那么用p表示数组元素时,有一定的差异.

例如:

p=p+3;

此时,指针变量p指向第四个数组元素a[3],那么p[0]、*(p+0)等价于a[3],而*(p-1)、p[-1]等价于a[2];*(p+1)、p[1]等价于a[4],依次类推.

【例5-3】输出一维数组中的所有元素。

main()

{

inta[]={1,2,3,4,5},*p,i;

p=a;/*将数组a的首地址赋值给指针变量p*/

for(i=0;i<5;i++)

printf("\n%d,%d,%d,%d",a[i],*(a+i),p[i],*(p+i));

}

程序分析与解释:

程序中的printf()函数展示了对一维数组元素的四种等价表示形式。

假如数组a的首地址是2000,那么p指向内存单元2000,则该数组在内存中存放形式及数组元素的表示形式如图5-4所示:

 

【例5-5】输入五个整数,使用指针变量将这五个数按从小到大排序后输出。

main()

{

inta[5],*pp,*p,*q,t;

for(p=a;p

scanf("%d",p);

for(p=a;p

{

pp=p;

for(q=p+1;q

if(*pp>*q)

pp=q;

if(pp!

=p)/*如果本轮比较出的较小值不等于*p,那么交换值*/

{

t=*p;

*p=*pp;

*pp=t;

}

}

for(p=a;p

printf("%d",*p);/*输出数组的值,并且每输出一个空一格*/

}

运行情况如下:

输入:

546512342

输出:

212345465

该问题的解决方法,采取的是在上一章所学的选择排序法来进行排序的.

5.2.2指向二维数组的指针

1.二维数组的地址

例如有定义语句:

inta[3][3];

✧二维数组名a是数组的首地址。

✧二维数组a包含三个行元素:

a[0]、a[1]、a[2]。

✧三个行元素的地址分别是:

a、a+1、a+2。

而a[0]、a[1]、a[2]也是地址量,是一维数组名,即*(a+0)、*(a+1)、*(a+2)是一维数组首个元素地址。

如图5-5:

 

2.二维数组元素的地址

a[0]、a[1]、a[2]是一维数组名,所以a[i]+j是数组元素的地址。

数组元素a[i][j]的地址可以表示为下列形式,&a[i][j]、a[i]+j、*(a+i)+j

如图5-6所示:

 

3.二维数组元素的表示法

数组元素可用下列形式表示:

a[i][j]、*(a[i]+j)、*(*(a+i)+j)

a是二维数组,根据C的地址计算方法,a经过两次*操作才能访问到数组元素。

所以:

*a是a[0],**a才是a[0][0]。

看一看,想一想若a是二维数组名请理解下列表示方法的含义

a

a[0],*(a+0),*a,&a[0][0]

a+1,&a[1]

a[1]+2,*(a+1)+2,&a[1][2]

a[0]是a[0][0]的地址,*a[0]是a[0][0]。

4.指向二维数组元素的指针变量

【例5-6】用指向数组元素的指针变量输出数组元素,请注意数组元素表示方法。

main()

{inta[3][4]={{0,1,2,3},{10,11,12,13},{20,21,22,23}},i,j,*p;

for(p=a[0],i=0;i<3;i++)

{for(j=0;j<4;j++)

printf("%4d",*(p+i*4+j));/*元素的相对位置为i*4+j*/

printf("\n");

}

}

程序分析与解释:

此程序定义了一个二维数组a和一个指向整型变量的指针变量p。

并将数组首地址赋值给指针变量p,通过改变变量i,j的值,来输出数组元素的值,整个过程中,指针变量p的值没有发生改变。

5.2.3指向字符串的指针变量

C语言将字符串是作为数组对待的,与数值型数组一样,我们也可用字符型的指针变量指向字符串,然后通过指针变量来访问字符串存贮区域。

设有如下语句:

char*cp;

cp="love”;

则cp指向字符串”love”常量的首字符’a’,如图5-7所示,程序中可通过cp来访问这一存贮区域。

 

1.指向字符串的指针变量的定义及初始化

【例5-7】通过初始化使指针指向一个字符串。

main()

{

charstr1[]="Goodmorning!

";

/*定义一个字符数组*/

char*str2="Goodnight!

";

/*定义一个指向字符串的指针变量*/

printf("%s\n",str1);

printf("%s\n",str2);

}

C语言中对字符常量是按照字符数组来处理的,在内存中开辟了一个字符数组用来存放该字符串常量。

对字符指针str2进行初始化,实际上是把字符串的第一个元素的地址(即存放字符串的首地址)赋给了str2,见图5-8。

有人认为str2是一个字符串变量,认为在定义的时候把"Goodnight!

"这几个字符赋给该字符串变量,这种认为是不对的。

定义指针变量str2部分:

char*str2="Goodnight!

";

等价于下面两行:

char*str2;

str2="Goodnight!

";

在输出时,要用:

printf(“%s\n”,str2);

%s是输出字符串格式控制符,在输出项要用字符指针变量名str2,则系统先输出指针所指向的字符,然后再自动使指针值加1,使之指向下一个字符,然后再输出一个字符,……,直到遇到字符串结束符’\0’为止。

注意,在内存中,字符串的最后都被自动加上了一个’\0’,如图5-8所示,因此在输出时能确定字符串的中止位置。

提醒

✧通过字符数组名或者字符指针变量可以输出一个字符串,而对数值型数组是不能企图用数组名输出它全部元素的,例如:

inta[3]={1,2,3};

printf(“%d”,a)

这种是错误的,数值型数组只能逐个元素输出。

✧对字符串中字符的存取,可以用下标法也可以用指针法。

【例5-8】已知字符串str,从中截取一子串。

要求该子串是从str的第m个字符开始,由n个字符组成。

【解题思路】

定义字符数组c存放子串,字符指针变量p用于复制子串,利用循环语句从字符串str截取n个字符。

考虑到几种特殊情况:

(1)m位置后的字符数有可能不足n个,所以在循环读取字符时,若读到‘\0’停止截取,利用break语句跳出循环。

(2)输入的截取位置m大于字符串的长度,则子串为空。

(3)要求输入的截取位置和字符个数均大于0,否则子串为空。

源程序如下:

main()

{

charc[80],*p,*str="Thisisastring.";

inti,m,n;

printf("m,n=");

scanf("%d,%d",&m,&n);

if(m>strlen(str)||n<=0||m<=0)

printf("NULL\n");

else

{

for(p=str+m-1,i=0;i

if(*p)

c[i]=*p++;

else

break;/*如读取到'\0'则停止循环*/

c[i]='\0';/*在c数组中加上子串结束标志*/

printf("%s\n",c);

}

}

5.3结构体

在前面,已经介绍过了基本数据类型的变量(如整型、字符型、浮点型变量),也介绍了一种构造类型数据——数组,其中数组中各元素属于同一种类型。

但是只有这些数据类型是不够的,有时需要将不同数据类型组合成一个有机的整体,以便引用。

这些组合在一个整体中的数据是互相联系的,例如:

一个学生的信息有学号、姓名、性别、年龄、住址、成绩等;一本图书的信息有分类编号、书名、作者、出版社、出版日期、价格、库存量等。

那么如何描述这些类型不同的相关数据呢?

在C语言中允许用户自己指定这样一种数据结构,它是由若干个类型不同的(当然也可以相同)的数据项组合在一起,称为结构体(structure)。

构成结构体的各个数据项称为结构体成员。

它相当于其他高级语言中的“记录”。

如果要用C语言编写实用程序,结构的知识是不可缺少的!

同时,结构还是C++等后续语言的基础。

5.3.1结构体的定义与引用

1.结构体定义的一般格式

struct结构体名

{

数据类型1成员名1;

数据类型2成员名2;

……

数据类型n成员名n;

};

提醒

✧不要忽略了最后的分号。

✧struct为关键字;

✧结构体名是用户定义的类型标识,用作结构体类型的标志。

✧结

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 党团工作 > 入党转正申请

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

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