《C语言程序设计新视角》课件第7章_第1页
《C语言程序设计新视角》课件第7章_第2页
《C语言程序设计新视角》课件第7章_第3页
《C语言程序设计新视角》课件第7章_第4页
《C语言程序设计新视角》课件第7章_第5页
已阅读5页,还剩204页未读 继续免费阅读

下载本文档

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

文档简介

第7章复合的数据类型7.1结构概念的引入 7.2结构体的描述与存储7.3结构的使用7.4结构体与函数的关系7.5共用体7.6枚举7.7typedef声明新的类型名7.8本章小结

【主要内容】

给出结构体类型变量的定义、使用规则及方法实例;

通过结构体与数组的对比,说明其表现形式与本质含义;

通过结构体类型与基本类型的对比,说明其表现形式与本质含义;

通过结构成员与普通变量的对比,给出其使用的规则;

读程序的训练;

自顶向下算法设计的训练;

结构的空间存储特点及调试要点。

【学习目标】

理解自定义数据类型结构体的意义;

掌握结构体的类型定义、变量定义、初始化、引用的步骤和方法;

掌握结构体与数组、指针、函数的关系;

了解联合的概念及其使用;

了解枚举的概念及其使用。

【引例1】先来回顾一下在第5章“函数”中举过的例5-7。

程序实现:

1 /*一维数组中求m到n项的和*/

2 #include<stdio.h>

3 intfunc(intb[],intm,intn)

7.1结构概念的引入

4 {

5 inti,sum=0;

6 for(i=m;i<=n;i++)

7 {

8 sum=sum+b[i];/*累加下标为m到n的元素*/

9 }

10 returnsum;

11 }

12

13 intmain()

14 {

15 intx,a[]={1,2,3,4,5,6,7,8,9,0};

16 intp=3,q=7;/*下标范围*/

17 x=func(a,p,q);

18 printf("%d\n",x);

19 return0;

20 }我们把上述题目主函数中的a数组由一维改为二维,子函数除了原先的功能外,再添加一个,把累加的结果放在每行的最后一个元素位置上,见图7.1。

图7.1a数组的信息

1 /*二维数组求每行m到n项的和*/

2 #include<stdio.h>

3 voidfunc(intb[],intm,intn,intlast);

4

5 voidfunc(intb[],intm,intn,intlast)

6 {

7 inti,sum=0;

8 for(i=m;i<=n;i++)

9 {

10 sum=sum+b[i]; /*累加下标为m到n的元素*/

11 }

12 b[last]=sum; /*把累加和放在指定位置*/

13 }

14

15 intmain()

16 {/*a数组由一维变为二维*/

17 inta[][10]={ {1,2,3,4,5,6,7,8,9,0},

18 {2,3,4,5,6,7,8,9,10,0},

19 {3,4,5,6,7,8,9,10,11,0}

20

};

21 intp=3,q=8,last=9;

22 /*a数组有3行信息,则调用3次func函数即可*/

23 for(inti=0;i<3;i++)

24 {

25 func(a[i],p,q,last);

26 }

27

28 for(i=0;i<3;i++) /*输出a数组内容*/

29 {

30 for(intj=0;j<10;j++)

31 {

32 printf("%4d",a[i][j]);

33 }

34 printf("\n");

35 }

36 return0;

37 }

程序结果:

12345678939

234567891045

3456789101151

【引例2】把引例1的二维数组的内容扩展成有实际意义的表格,如表7.1所示,求出每个学生的总分,并填在此表中。

表7.1学

表与二维数组相比,这个表格中的数据项并不都是同一种类型,要对这样的表格进行处理,根据计算机解题的通用规则,首要的问题是如何把这样的表格存储到机器中。计算机解题的通用规则为:用合适的数据结构存储数据;用相应的算法处理数据。

(1)如何将表7.1所示的数据表格存储到机器中?

答:根据已有数组存储的概念,可以把表格中的每列信息都构造成一个一维数组,但这样做,显然程序处理起来是非常麻烦的,首先就是主函数不能方便地把一行信息传递给子函数。借助二维数组的处理方式,我们希望一次就能传递表格的一行信息。

(2)如何把不同类型的一行数据组合在一起?

答:表格中的数据有多行,只要把一行的信息如何组合存储的方式分析清楚即可,因为多行信息只是一行的多次重复。因此,现在解决问题的关键变成,如何把相关的一组不同类型的数据“打包”放在一个连续的空间,传递时只要传递这个空间的起始地址即可。

系统要给用户的“打包”数据分配空间,首先就得定义它的存储尺寸,即数据的类型。然而数据表中的内容是用户根据需要确定的,系统无法预先得知,这就需要用户自己“构造”出数据表的类型,然后才能进行存储空间的分配。

根据上述结论,可列出所有已知的条件和希望的结果:

(1)有多个数据项,每个数据项都可以用已有的数据类型描述;

(2)数据项的多少、内容是由用户自己确定的;

(3)希望上述各数据项“组合”在一起,有连续的存储空间,可以作为一个整体来方便传址;

(4)要求每个数据项可以单独引用。

根据上面这些条件和要求以及存储三要素,可给出这种“组合的数据”类型与变量的特征如表7.2所示。

表7.2组合数据的特点

说明:类型的名字之所以是“关键字+标识符”,是要有一个特别约定的关键词来表明这是一个类型,又因为这个类型是用户自己定义的,所以类型名中应该有用户自己命名的成分,以和其他类似的“组合的数据”类型名区别。

这种“组合的数据”在C语言中被称为结构体,其特点如下:

(1)结构体是C语言中的构造类型,是由不同数据类型的数据组成的集合体;

(2)结构体为处理复杂的数据结构提供了手段;

(3)结构体为函数间传递不同类型的参数提供了便利。

7.2.1结构体的类型定义

结构体(struct):由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。

结构体由若干不同类型的数据项组成,构成结构体的各个数据项称为结构体成员。7.2结构体的描述与存储结构体类型定义描述结构的组织形式,是一种用户自定义的类型,同C的基本类型中类型的概念一样,只是一个存储尺寸,不分配内存。

结构体类型定义形式如下:

struct结构体名

{数据类型1成员名1;

数据类型2成员名2;

数据类型n成员名n;

};说明:

(1)结构体类型名:struct与结构体名合起来表示结构体类型名。struct是关键字,结构体名由用户用标识符标示。结构体类型也可简称结构类型。

(2)结构体成员:结构体中的一个数据项。结构体成员的数据类型可以是C语言允

许的所有类型。

【例7-1】结构体类型定义的例子。将图7.2中的各数据项组合在一个结构里。

图7.2学生信息数据项程序如下:

structstudent /*struct为结构关键字,student为结构名*/

{

intStudentId; /*学号*/

charStudentName[10]; /*姓名*/

charStudentSex[3]; /*性别*/

intTimeOfEnter; /*入学时间*/

intScore_1; /*成绩1*/

intScore_2; /*成绩2*/

intScore_3; /*成绩3*/

intScore_4; /*成绩4*/

}; 7.2.2结构体变量定义及初始化

结构体变量定义有下面三种格式,在实际编程时可以根据需要选用。

结构体变量定义格式1:

struct结构类型名变量名表;

结构体变量定义格式2:

struct结构类型名

{类型标识符成员名1;

类型标识符成员名2;

类型标识符成员名n;

}变量名表;结构体变量定义格式3:

struct

{类型标识符成员名1;

类型标识符成员名2;

类型标识符成员名n;

}变量名表;说明:第三种格式用无名结构体直接定义变量只能用一次,无法在后续程序中再使用这个结构类型。

【例7-2】结构变量定义的例子。结构变量定义语句如下:

structstudentcom[30],*sPtr,x;

结构类型structstudent是在例7-1中已经定义的。

定义语句各项的含义见表7.3。

表7.3结

结构变量初始化格式如下:

struct结构类型名变量名={初始数据}

【例7-3】结构变量初始化的例子。结构类型structstudent是在例7-1中已经定义的。

structstudentcom[30]

={{1,"赵壹","男",1999,90,83,72,82},

{2,"钱贰","男",1999,78,92,88,78},

{3,"孙叁","女",1999,89,72,98,66},

{4,"李肆","女",1999,78,95,87,90}

};/*结构数组的初始化*/

structstudent*sPtr;/*定义结构指针*/

sPtr=com;/*结构指针指向结构数组*/7.2.3结构体成员引用方法

结构体成员引用有三种方法:

方法一:结构体变量名.成员名;

方法二:结构指针名→成员名;

方法三:(*结构指针名).成员名。

【例7-4】结构体成员引用方法。结构类型定义如下:

structstudent

{

intStudentId;

charStudentName[10];

charStudentSex[3];

intTimeOfEnter;

intScore[4];

inttotal;

}

用structstudent结构类型定义的结构变量、结构成员的引用见表7.4。

表7.4结构成员引用方法7.2.4结构变量的空间分配及查看方法

【例7-5】结构变量、结构指针和结构数组的查看。

1 #include<stdio.h>

2 intmain()

3 {

4 structstudent

5 {

6 intStudentId;

7 charStudentName[10];

8 charStudentSex[3];

9 intTimeOfEnter;

10 intScore[4];

11 inttotal;

12 };

13

14 structstudentx;

15 structstudentcom[10]

16 ={{1,"赵壹","男",1999,90,83,72,82},

17 {2,"钱贰","男",1999,78,92,88,78},

18 {3,"孙叁","女",1999,89,72,98,66},

19 {4,"李肆","女",1999,78,95,87,90}

20 };

21 structstudent*sPtr;

22

23 sPtr=com;

24 x=com[0];

25 return0;

26 }

【解】(1)结构变量定义:

structstudentx;(程序第14行)

对于变量x,系统将会以什么样的方式给它分配空间呢?C规定按结构类型中成员的前后顺序及大小来给结构变量分配空间。结构变量X的成员如图7.3所示。

图7.3结构变量x的成员结构变量x的空间分配在程序第24行未执行前的情形参见图7.4~图7.7。图7.5是点开图7.4中StudentName前的“+”号,显示x的成员——数组Student-Name的元素。

图7.4结构变量x的空间分配(总)

图7.5结构变量x的空间分配(1)

图7.6结构变量x的空间分配(2)

图7.7结构变量x的空间分配(3)

(2)结构数组定义:

structstudentcom[10];(程序第15行至第20行)

指针赋值:

sPtr=com;(程序第23行)

sPtr执向com的位置及sPtr偏移9后指向的位置见图7.8。

图7.8结构数组com的空间示意图

图7.9结构数组com的空间分配

图7.10结构指针sPtr的指向

图7.11结构指针sPtr+3的指向

图7.12x=com[0]赋值后的情形

内存地址对齐(alignment)

为了提高CPU访问内存的效率,程序语言的编译器在做变量的存储分配时就进行了分配优化处理,对于基本类型的变量,其优化规则(也称为“对齐”规则)如下:

变量地址%N=0

其中对齐参数N=sizeof(变量类型)。

注:不同的编译器,具体的处理规则可能不一样。

结构体空间分配规则(VC++6.0环境)

1)结构成员存放顺序

结构体的成员在内存中顺序存放,所占内存地址依次增高,第一个成员处于低地址处,最后一个成员处于最高地址处。

2)结构对齐参数

(1)结构体一个成员的对齐参数:

N=min(sizeof(该成员类型),n)

注:n为VC++6.0中可设置的值,默认为8字节。

(2)结构体的对齐参数M:M=结构体中所有成员的对齐参数中的最大值。

3)结构体空间分配规则

(1)结构体长度L:满足条件L%M=0(不够要补足空字节)。

(2)每个成员地址x:满足条件x%N=0(空间剩余,由下一个成员做空间补充)。结构内的成员空间分配以M为单位开辟空间单元;若成员大小超过M,则再开辟一个M单元;若此单元空间剩余,则由下一个成员按对齐规则做空间补充(结构体嵌套也是一样的规则)。

【例7-6】结构存储空间及内存对齐的查看例子1。结构体成员是基本数据类型时的内存对齐。

struct

{

short

a1;

short

a2;

short

a3;

}A={1,2,3};

struct

{

long

a1;

short

a2;

}B={4,5};

struct

{

shorta1;

longa2;

}C={6,7};

sizeof(A)=6,sizeof(B)=8,sizeof(C)=8,为什么是这样?

注:sizeof(short)=2,sizeof(long)=4。

图7.13为例7-6的查看步骤1,结构体A的对齐参数M=sizeof(short)=2(byte)。

图7.13例7-6查看步骤1

图7.14例7-6查看步骤2

图7.15例7-6查看步骤3

【例7-7】结构存储空间及内存对齐的查看例子2。结构体成员是构造数据类型时的内存对齐。设

structstudentx={1,"赵壹","男",3,4,5,6,7};

图7.16中,Memory窗口的地址是结构变量x的起始地址。

图7.16例7-7查看步骤

x的存储空间长度=0x12ff7c-0x12ff58+4=0x28=40(byte)

x成员定义长度和=(int+char*10+char*3+int+int*4)=37(byte)

二者相差3

byte,即存在如图7.17所示的内存“空洞”,这是如何产生的呢?

结构体x的对齐参数M=sizeof(int)=4

注意:

(1)0x12FF64、0x12FF65两个单元放的是StudentName[8]和StudentName[9]。

图7.17内存“空洞”

(2)StudentSex的起始地址是0x12FF66。因为StudentSex的对齐参数N=min(sizeof(该成员类型),8)=sizeof(char)=1,0x12FF66%N=0,所以StudentSex的三个元素从0x0x12FF66开始存储。

(3)TimeOfEnter的起始地址是0x12FF6C。因为TimeOfEnter的对齐参数

N=sizeof(int)=4,StudentSex存储后的起始地址是0x12FF69,0x12FF69至0x12FF6B都不是4的整数倍

(如图7.18所示),而0x12FF6C是4的整数倍,所以TimeOfEnter的起始地址是0x12FF6C。因此,StudentSex后的3

byte内存“空洞”是由于TimeOfEnter的“对齐”产生的。图7.18地址模4运算

【例7-8】结构与数组类比的例子。请写出一个程序,找出表7.5中的最高成绩,显示此组信息,并将之与表中的第一列信息交换。7.3结 构 的 使 用

表7.5数据表

表格中的数据如何存储?

答:根据计算机解题的通用规则,我们首先要解决的问题是如何把表格中的数据按什么形式存储到机器中,然后才能在相应的存储结构中进行算法处理。

数据结构设计可采用以下三种方式:

(1)用一维数组。

成绩数组:intscore[6]={90,80,65,95,75,97};

座位数组:intset[6]={1,2,3,4,5,6};

(2)用二维数组。成绩与座位的组合:

intscore[2][6]={{90,80,65,95,75,97},{1,2,3,4,5,6}};

用一维或二维数组的方式存储数据的规则我们已经熟悉了,即把同类型的数据按序存放,元素用数组名配合下标引用。

(3)用结构。

方式1:

structnode{

intscore[6];

intset[6];}

structnodex={{90,80,65,95,75,97},{1,2,3,4,5,6}}

方式2:

structnode{

intscore;

intset;}

structnodey[6]={{90,1},{80,2},{65,3},{95,4},{75,5},{97,6}};用结构的方式存储的思路是,把相关的一组数据“打包”放在一起。结构的类型是用户自己定义的,结构的空间是在定义结构类型变量时分配的。

按照结构的形式,把数据存储到内存后,要对它们进行处理,就引出了数据如何引用的问题。表7.6给出了一维数组、二维数组、结构三种数据的组织形式以及其中数据项的引用形式。

表7.6数据存储及引用

表7.7x与y的存储

表7.8例7-8伪代码

【方法1】

1 /*例7-8用一维数组实现*/

2 #include<stdio.h>

3 #defineMAX6

4

5 intmain()

6 {

7 intscore[MAX]={90,80,65,95,75,97};

8 intset[MAX]={1,2,3,4,5,6};

9 intmax,num;

10 inttemp1,temp2;

11

12 /*在score中找最大值,并将之记录在max中,对应下标值记录在num中*/

13 max=score[0];/*取第一组值做比较基准*/

14 num=1;

15 for(inti=1;i<MAX;i++)

16 {

17 if(max<score[i])

18 {

19 max=score[i];

20 num=set[i];

21 }

22 }

23

24 /*最大值与第一个值交换*/

25 temp1=score[0];

26 temp2=set[0];

27 score[0]=max;

28 set[0]=num;

29 score[num-1]=temp1;

30 set[num-1]=temp2;

31

32 /*输出*/

33 printf("第1名:%d号,%d分\n",set[0],score[0]);

34 return0;

35 }

程序结果:

第1名:6号,97分

【方法2】

1 /*例7-8用二维数组实现*/

2 #include<stdio.h>

3 #defineMAX6

4 intmain()

5 {

6 intscore[2][MAX]=

7 {{90,80,65,95,75,97},

8 {1,2,3,4,5,6}

9 };

10 intmax,num;

11 inttemp1,temp2;

12

13 /*在score中找最大值,并将之记录在max中,对应下标值记录在n中*/

14 max=score[0][0];/*取第一组值做比较基准*/

15 num=1;

16 for(inti=1;i<MAX;i++)

17 {

18 if(max<score[0][i])

19 {

20 max=score[0][i];

21 num=score[1][i];

22 }

23 }

24

25 /*最大值与第一个值交换*/

26 temp1=score[0][0];

27 temp2=score[1][0];

28 score[0][0]=max;

29 score[1][0]=num;

30 score[0][num-1]=temp1;

31 score[1][num-1]=temp2;

32

33 /*输出*/

34 printf("第1名:%d号,%d分\n",score[1][0],score[0][0]);

35 return0;

36 }

程序结果:

第1名:6号,97分

【方法3】

1 /*例7-8用结构方式1实现*/

2 #include<stdio.h>

3 #defineMAX6

4 intmain()

5 {

6 structnode

7 {

8 intscore[MAX];

9 intset[MAX];

10 }x={{90,80,65,95,75,97},{1,2,3,4,5,6}};

11 intmax,num;

12 inttemp1,temp2;

13

14 /*在score中找最大值,并将之记录在m中,对应下标值记录在n中*/

15 max=x.score[0];/*取第一组值做比较基准*/

16 num=1;

17 for(inti=1;i<MAX;i++)

18 {

19 if(max<x.score[i])

20 {

21 max=x.score[i];

22 num=x.set[i];

23 }

24 }

25

26 /*最大值与第一个值交换*/

27 temp1=x.score[0];

28 temp2=x.set[0];

29 x.score[0]=max;

30 x.set[0]=num;

31 x.score[num-1]=temp1;

32 x.set[num-1]=temp2;

33

34 /*输出*/

35 printf("第1名:%d号,%d分\n",x.set[0],x.score[0]);

36 return0;

37 }

程序结果:

第1名:6号,97分

【方法4】

1 /*用结构方式2实现*/

2 #include<stdio.h>

3 #defineMAX6

4 intmain()

5 {

6 structnode

7 {

8 intscore;

9 intset;

10 }y[6]={{90,1},{80,2},{65,3},{95,4},{75,5},{97,6}};

11 intmax,num;

12 inttemp1,temp2;

13

14 /*在score中找最大值,并将之记录在m中,对应下标值记录在n中*/

15 max=y[0].score;/*取第一组值做比较基准*/

16 num=1;

17 for(inti=1;i<MAX;i++)

18 {

19 if(max<y[i].score)

20 {

21 max=y[i].score;

22 num=y[i].set;

23 }

24 }

25

26 /*最大值与第一个值交换*/

27 temp1=y[0].score;

28 temp2=y[0].set;

29 y[0].score=max;

30 y[0].set=num;

31 y[num-1].score=temp1;

32 y[num-1].set=temp2;

33

34 /*输出*/

35 printf("第1名:%d号,%d分\n",y[0].set,y[0].score);

36 return0;

37 }

程序结果:

第1名:6号,97分

【例7-9】顺序结构程序的例子的改进。从键盘输入四个学生的学号和英语考试成绩,打印这四人的学号和成绩,最后输出四人的英语平均成绩。

1 /*顺序结构程序的例子的改进*/

2 #include<stdio.h>

3 intmain()

4 {

5 structnode

6 {

7 intnumber[4];

8 floatgrade[4];

9 }stu;

10

11 floatgrade=0;

12 inti;

13 /*输入四个学生的学号和英语考试成绩*/

14 for(i=0;i<4;i++)

15 {

16 printf("inputnumber:\n");

17 scanf("%d",&stu.number[i]);

18 printf("inputgrader:\n");

19 scanf("%f",&stu.grade[i]);

20 grade=grade+stu.grade[i];

21 }

22 /*打印四人的学号和成绩及平均成绩*/

23 printf("numbergrade\n");

24 for(i=0;i<4;i++)

25 {

26 printf("%d:%0.1f\n",stu.number[i],stu.grade[i]);

27 }

28 printf("average=%0.1f\n",grade/4);

29 return0;

30 }

程序结果:

inputnumber:

101

inputgrader:

98

inputnumber:

102

inputgrader:

87

inputnumber:

103

inputgrader:

67

inputnumber:

104

inputgrader:

92

numbergrade

101:98.0

102:87.0

103:67.0

104:92.0

average=86.0

把数据按照结构的形式存储后,这个程序从形式上看,比“顺序结构程序”要简洁。

【例7-10】结构的例子2。请设计一个统计选票的程序。现设有三个候选人的名单,见表7.9,请分别统计出他们各得票的多少。由键盘输入候选人的名字来模拟唱票过程,选票数为N(注:每次只能从三个候选者中选择一人)。

表7.9选票【解】伪代码见表7.10。

表7.10例7-10伪代码

1 /*统计选票*/

2 #include<stdio.h>

3 #include<string.h>

4 #defineN50/*投票人数*/

5 structperson

6 { charname[20];/*候选人姓名*/

7 intsum;/*得票数*/

8 }

9

10 intmain()

11 {

12 structpersona[3]

13 ={"Zhang",0,"Tong",0,"Wang",0};/*选票结构*/

14 inti,j;

15 charin_name[20];

16

17 for(i=1;i<=N;i++)/*N位投票人,处理N次*/

18 {

19 scanf("%s",in_name);/*输入候选人名*/

20 for(j=0;j<3;j++)/*选中的候选者得票数加1*/

21 if(strcmp(in_name,a[j].name)==0)

22 {

23 a[j].sum++;

24 }

25 }

26 for(i=0;i<3;i++)/*输出结果*/

27 {

28 printf("%s,%d\n",a[i].name,a[i].sum);

29 }

30 return0;

31 }

【例7-11】结构的例子3——对“函数读程练习1的例子”做一改进。

原题目:处理3个学生四门课程的成绩,成绩存储在二维数组studentGrades中。

(1)求所有成绩中的最低、最高成绩;

(2)每个学生的平均成绩;

(3)输出结果。

intstudentGrades[STUDENTS][EXAMS]

=

{{

77,68,86,73

},

{

96,87,89,78

},

{

70,90,86,81

}

};

改进的题目:已知N个学生的学号、姓名及四门课程的成绩,如表7.11所示。

(1)求所有成绩中的最低、最高成绩;

(2)求每个学生的总成绩、平均成绩;

(3)打印全班成绩单。

表7.11学

【解】

(1)数据存放:选结构存储。

#defineN50

structstu

{

intnum;

charname[10];

floatmath;

floatphys;

floateng;

floatpro;

floattotal;

floatave;

};

structstua[N]

={ {1,"Zhao",77,68,86,73,0,0},

{2,"Qian",96,87,89,78,0,0},

{3,"Sun",70,90,86,81,0,0}

};

(2)算法描述:伪代码见表7.12。

表7.12例7-11伪代码

(3)程序实现关键点分析:

inti;

floatlowGrade=100; /*初始化为可能的最高分数*/

floathighGrade=0; /*初始化为可能的最低分数*/

floattotal=0;

/*找比lowGrade低的值,记在lowGrade中*/

if(lowGrade>a[i].math)lowGrade=a[i].math;

if(lowGrade>a[i].phys)lowGrade=a[i].phys;

if(lowGrade>a[i].eng)lowGrade=a[i].eng;

if(lowGrade>a[i].pro)lowGrade=a[i].pro;

/*找比highGrade低的值,记在highGrade中*/

if(highGrade<a[i].math)highGrade=a[i].math;

if(highGrade<a[i].phys)highGrade=a[i].phys;

if(highGrade<a[i].eng)highGrade=a[i].eng;

if(highGrade<a[i].pro)highGrade=a[i].pro;

total=a[i].math+a[i].phys+a[i].eng+a[i].pro;

a[i].total=total; /*在表中填总成绩*/

a[i].ave=total/4; /*在表中填平均成绩*/

每项成绩的引用形式都很繁琐,最好能以一种简洁的方式表示各项成绩。如何改进?

改进方法:设置一个gradePtr指针,指向第一个成绩,见图7.19。

图7.19对成绩引用的改进

gradePtr[0]=a[0].math;

gradePtr[1]=a[0].phys;

gradePtr[2]=a[0].eng;

gradePtr[3]=a[0].pro;

因为在结构中,成绩各项的数据类型都是相同的,所以也是连续存储的。各项成绩通过指针的引用,就变得有规律了。

float*gradePtr;

gradePtr=&a[0].math;

total=0;

for(i=0;i<4;i++) /*循环结构中一个学生的各科成绩*/

{

if(gradePtr[i]<lowGrade)lowGrade=gradePtr[i];

if(gradePtr[i]>highGrade)highGrade=gradePtr[i];

total+=gradePtr[i];

}

a[0].total=total; /*在表中填总成绩*/

a[0].ave=total/4; /*在表中填平均成绩*/

或者

float*gradePtr;

gradePtr=&a[0].math;

total=0;

for(i=0;i<4;i++,gradePtr++)

{

if(*gradePtr<lowGrade)lowGrade=*gradePtr;

if(*gradePtr>highGrade)highGrade=*gradePtr;

total+=*gradePtr;

}

a[0].total=total;

a[0].ave=total/4;

完整的程序如下:

1 /*对结构表中成绩的统计*/

2 #include<stdio.h>

3 #defineN3

4 structstu

5 {intnum;

6 charname[10];

7 floatmath;

8 floatphys;

9 floateng;

10 floatpro;

11 floattotal;

12 floatave;

13 };

14

15 intmain()

16 {

17 structstua[N]

18 ={ {1,"Zhao",77,68,86,73,0,0},

19 {2,"Qian",96,87,89,78,0,0},

20 {3,"Sun",70,90,86,81,0,0}

21 };

22 float*gradePtr;

23 floatlowGrade=100; /*初始化为可能的最高分数*/

24 floathighGrade=0; /*初始化为可能的最低分数*/

25 floattotal=0; /*考试成绩总和*/

26

27 for(intj=0;j<N;j++) /*循环结构中的行*/

28 {

29 gradePtr=&a[j].math;

30 total=0;

31 for(inti=0;i<4;i++) /*循环结构中一行的各个成绩*/

32 {

33 if(gradePtr[i]<lowGrade)lowGrade=gradePtr[i];

34

if(gradePtr[i]>highGrade)highGrade=gradePtr[i];

35 total+=gradePtr[i];

36 }

37 a[j].total=total;

38 a[j].ave=total/4; /*计算平均成绩*/

39 }

40 printf("\nLowestrade:%.1f\nHighestgrade:%.1f\n\n",

41 lowGrade,highGrade);

42 /*输出成绩表格*/

43 printf("numnamemath.P.totalave\n");

44 for(inti=0;i<N;i++)

45 {

46 printf("%3d%6s%6.1f%6.1f%6.1f%6.1f%6.1f%6.1f\n",

47

a[i].num,a[i].name,a[i].math,a[i].phys,a[i].eng,

48 a[i].pro,a[i].total,a[i].ave);

49 }

50 return0;

51 }/*结束main*/

程序结果:

Lowestgrade:68.0

Highestgrade:96.0

numnamemath.Phys.eng.pro.totalave

1Zhao77.068.086.073.0304.076.0

2Qian96.087.089.078.0350.087.5

3Sun70.090.086.081.0327.081.8

和普通变量一样,结构体变量、结构体指针均可作为函数的参数和返回值,具体情形参见表7.13。7.4结构体与函数的关系

表7.13函数中结构体作参数的方式

【例7-12】结构变量作形参的例子。

1 /*结构变量做形参*/

2 #include<stdio.h>

3

4 structstudent

5 { intnum;

6 floatgrade;

7 };

8

9 structstudentfunc1(structstudentstu) /*形参为结构变量*/

10 {

11 stu.num=101;

12 stu.grade=86;

13 return(stu); /*返回结构变量*/

14 }

15

16 intmain()

17 {

18 structstudentx={0,0};

19 structstudenty;

20

21 y=func1(x); /*实际参数为整个结构变量*/

22 return0;

23}

图7.20~图7.23分别为例7-12的调试步骤1~调试步骤4。

图7.20例7-12调试步骤1图7.21中,值传递,形参stu的地址为0x12ff14,与实参存储单元不是同一个。实参的值被拷贝了一份,放在形参中。

图7.22中,结构成员在子函数func1中被修改。

图7.23中,返回主函数,结构变量y接收func1返回的结构变量的值。

图7.21例7-12调试步骤2

图7.22例7-12调试步骤3

图7.23例7-12调试步骤4注意:x、y与stu三者的存储单元地址都是各分单元的,x的值并未被修改。

【例7-13】返回值是结构指针的例子。

1 /*返回值是结构指针*/

2 #include<stdio.h>

3

4 structstudent

5 {intnum;

6 floatgrade;

7 };

8

9 structstudent*func2(structstudentstu)

10 {

11 structstudent*str=&stu;

12 str->num=101;

13 str->grade=86;

14 return(str); /*返回结构指针*/

15 }

16

17 intmain()

18 {

19 structstudentx={0,0};

20 structstudent*stuPtr;

21

22 stuPtr=func2(x);

23 return0;

24 }图7.24~图7.31分别为例7-13的调试步骤1~调试步骤8。

图7.24中,注意实参x的地址为0x12ff78。

图7.25中,注意形参stu的地址为0x12ff20。

图7.26中,修改stu结构中的成员值。

图7.27中,主函数中sutPtr接收返回的局部量str的值。

图7.24例7-13调试步骤1

图7.25例7-13调试步骤2

图7.26例7-13调试步骤3

图7.27例7-13调试步骤4注意:在第6章“指针”中讨论过关于不要返回局部量的地址的问题。此程序若以结构体指针为参数,可以改进如下:

1 /*以结构体指针为形式参数*/

2 #include<stdio.h>

3

4 structstudent

5 {intnum;

6 floatgrade;

7 };

8

9 voidfunc3(structstudent*str)

10 {

11 str->num=101;

12 str->grade=86;

13 }

14

15 intmain()

16 {

17 structstudentx={0,0};

18

19 func3(&x);

20 return0;

21}

图7.28~图7.31为改进后程序的调试步骤。

图7.28中,实参为结构变量的地址0x12ff78。

图7.29中,形参为结构指针,指向实参单元x。

图7.30中,子函数func3修改0x12ff78地址中的结构成员数据。

图7.28例7-13调试步骤5

图7.29例7-13调试步骤6

图7.30例7-13调试步骤7

图7.31例7-13调试步骤8

结构变量与结构指针在函数的信息传递中的使用方法及原则与普通变量及指针是一样的。

【例7-14】结构成员作形参的例子。已知一个班学生信息如表7.14所示,要求在主函数中赋初值及打印结果,在子函数中求出一个人的总成绩和平均成绩。

表7.14成绩表

【解】(1)数据存放:结构存储。

#defineN50

structstu

{intnum;

charname[10];

floatmath;

floatphys;

floateng;

floatpro;

floattotal;

floatave;

};

structstua[N]

={ {1,"Zhao",77,68,86,73,0,0},

{2,"Qian",96,87,89,78,0,0},

{3,"Sun",70,90,86,81,0,0}

};

(2)算法分析:子函数的功能是实现总分和均分的计算,因此只需把各科成绩信息传递给子函数即可,结构中的学号、姓名等信息是不必传递的。设

structstup;

float*gradePtr;

p=a; /*ptr指针指向结构表格的一行*/

gradePtr=&a[0].math; /*gradePtr指向成绩起始地址*/

指针gradePtr和p的指向见图7.32。

图7.32数据及引用成绩信息传递方法设计:由于各科的成绩数据类型都一样,在结构中是连续存储的,因此把成绩信息作地址传递即可。子函数设计见表7.15。

表7.15例7-14子函数设计说明:

(1)输入信息是成绩,有多个,可能的方案有传值和传址两种,这里采用传址方式;形式参数为float型指针,实际参数为结构成员math的地址。

(2)输出信息有两个,这里采用形参共用地址的方式,故就不用return了,因此函数的类型为void。

(3)程序实现:

1 /*结构成员作形参*/

2 #include<stdio.h>

3 #defineN3

4 voidcalculate(float*gradePtr);

5

6 structstu

7 { intnum;

8 char*name;

9 floatmath;

10 floatphys;

11 floateng;

12 floatpro;

13 floattotal;

14 floatave;

15 };

16

17 intmain()

18 {

19 structstua[N]

20 ={ {1,"Zhao",77,68,86,73,0,0},

21 {2,"Qian",96,87,89,78,0,0},

22 {3,"Sun",70,90,86,81,0,0}

23 };

24 structstu*p=a;

25 inti;

26

27 printf("NameNum.Math.Phys.Eng.Pro.Total.Ave\n");

28 for(p=a,i=0;i<N;i++,p++)

29 {

30 calculate(&(*p).math);

31 printf("%4d%6s%7.2f%7.2f%7.2f%7.2f%7.2f%7.2f\n",

32 (*p).num,(*p).name,(*p).math,(*p).phys,

33 (*p).eng,(*p).pro,(*p).total,(*p).ave);

34 }

35 return0;

36 }

37

38 /*计算一个学生的总成绩和平均成绩*/

39 voidcalculate(float*gradePtr)

40 {

41 floatsum=0;

42 inti;

43 for(i=0;i<4;++i,++gradePtr)

44 {

45 sum+=*gradePtr;

46 }

47 *gradePtr=sum; /*求总成绩*/

48 gradePtr++;

49 *gradePtr=sum/4; /求平均成绩*/

50 }

程序结果:

NameNum.Math.Phys.Eng.Pro.Total.Ave

1Zhao77.0068.0086.0073.00304.0076.00

2Qian96.0087.0089.0078.00350.0087.50

3Sun70.0090.0086.0081.00327.0081.75

共用体(联合体)是指几个不同时出现的变量成员共享一块内存单元。当若干变量每次只使用其中之一时,可以采用“共用体”(union)数据结构。给共用体数据中各成员分配同一段内存单元,设置这种数据类型的主要目的就是节省内存。7.5共用体

1.共用体类型的定义

共用体类型定义的一般形式为:

union共用体名

{

类型名1成员名1;

类型名2成员名2;

类型名n成员名n;

}说明:

(1)union为关键字。

(2)和struct声明一样,union声明仅仅创建了一个类型,在任何函数之外加union和struct声明,并不会创建实际变量。

例如:

unionnumber

{

intx;

charch;

floaty;

};

与struct成员不同的是,union中的成员x、ch和y具有同样的地址,如图7.33所示。sizeof(unionnumber)取决于占空间最多的成员变量y。

图7.33共用体成员共用同一个地址共用体的特点及与结构体的关系见表7.16。

表7.16共用体的特点及与结构体的关系2.共用体变量的定义

与结构体变量相同,共用体变量定义的一般形式为:

union共用体名

{

类型名1成员名1;

类型名2成员名2;

类型名n成员名n;

}变量名表;

3.共用体成员的引用方式

共用体成员引用方式有两种:

方式一:共用体变量名.成员名;

方式二:共用体指针名->成员名。

【例7-15】共用体的例子1。

#include<stdio.h>

intmain()

{

unionnumber /*定义共用体类型*/

{

intx;

charch;

floaty;

};

unionnumberunit; /*定义共用体变量*/

unit.x=1; /*共用体成员引用*/

unit.ch='a';

unit.y=2;

return0;

}

图7.34~图7.36所示分别为例7-15的调试步骤1~调试步骤3。

由图7.34可以看出三个共用体成员的地址都是0x12ff7c,其中显示了给成员x赋值1时的情形。

图7.35显示了给成员ch赋值‘a’

时的情形。

图7.36显示了给成员y赋值2时的情形。注意Memory中的值显示的是0x40000000,这是什么原因呢?请看下面的“思考与讨论”。

图7.34例7-15调试步骤1

图7.35例7-15调试步骤2

图7.36例7-15调试步骤3

图7.36中,float型变量y的值是2,为什么在内存中显示为0x40000000?

答:根据第2章中介绍的IEEE754标准,按float占32位的情形,实数2的存储形式见表7.17,即为0x40

温馨提示

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

评论

0/150

提交评论