第9章 结构体和共用体1207.docx

上传人:b****3 文档编号:27044769 上传时间:2023-06-26 格式:DOCX 页数:36 大小:548.17KB
下载 相关 举报
第9章 结构体和共用体1207.docx_第1页
第1页 / 共36页
第9章 结构体和共用体1207.docx_第2页
第2页 / 共36页
第9章 结构体和共用体1207.docx_第3页
第3页 / 共36页
第9章 结构体和共用体1207.docx_第4页
第4页 / 共36页
第9章 结构体和共用体1207.docx_第5页
第5页 / 共36页
点击查看更多>>
下载资源
资源描述

第9章 结构体和共用体1207.docx

《第9章 结构体和共用体1207.docx》由会员分享,可在线阅读,更多相关《第9章 结构体和共用体1207.docx(36页珍藏版)》请在冰豆网上搜索。

第9章 结构体和共用体1207.docx

第9章结构体和共用体1207

第1章结构体和共用体

学习目标

掌握结构体类型与变量的定义及初始化

掌握结构体变量的引用

掌握结构体与数组、指针、函数等结合使用

掌握共用体变量的定义与引用

理解结构体与共用体的内存分配机制

前面章节所学的数据类型都是分散的、互相独立的,例如定义inta和charb两个变量,这两个变量是毫无内在联系的,但在实际生活和工作中,经常需要处理一些关系密切的数据,例如,描述公司一个员工的姓名、部门、职位、电话、E-mail地址等,由于这些数据的类型各不相同,因此,要想对这些数据进行统一管理,仅靠前面所学的基本类型和数组都很难实现。

为此,C语言提供了另外两种构造类型,分别是结构体和共用体,本章将围绕这两种构造类型进行详细地讲解。

结构体类型和结构体变量

结构体类型定义

结构体是一种构造数据类型,把不同类型的数据整合在一起,每一个数据都称为该结构体类型的成员。

在程序设计中,使用结构体类型时,首先要对结构体类型的组成进行描述,结构体类型的定义方式如下所示:

struct结构体类型名称

{

数据类型成员名1;

数据类型成员名2;

……

数据类型成员名n;

};

在上述语法结构中,“struct”是定义结构体类型的关键字,其后是所定义的“结构体类型名称”,在“结构体类型名称”下的大括号中,定义了结构体类型的成员项,每个成员是由“数据类型”和“成员名”共同组成的。

例如,为了描述一组学生信息,该信息由学号(num)、姓名(name)、性别(sex)、年龄(age)、地址(address)等组成,我们可以使用下列语句定义一个名称为Student的结构体类型:

structStudent

{

intnum;

charname[10];

charsex;

intage;

charaddress[30];

};

在上述结构体类型的定义中,结构体类型Student由5个成员组成,分别是num、name、sex、age和address。

值得一提的是,结构体类型中的成员,也可以是一个结构体变量,例如,在学生信息中增加一项出生日期的信息,具体代码如下:

structDate

{

intyear;

intmonth;

intday;

};

structStudent

{

intnum;

charname[10];

charsex;

structDatebirthday;

}stu1;

在上述代码中,我们首先定义了结构体类型Date,该结构体类型由year、month、day三个成员组成;然后定义了结构体变量stu1,其中的成员birthday是Date结构体类型。

student的类型结构如图9-1所示。

图9-1结构体类型Student的类型结构

注意:

1、结构体类型定义以关键字struct开头,后面跟的是结构体类型的名称,该名称的命名规则与变量名的命名规则相同。

2、定义好一个结构体类型后,并不意味着立即分配一块内存单元来存放各个数据成员,它只是告诉编译器,该结构体类型是由哪些数据类型的成员构成,各占多少个字节,按什么格式存储,并把它们当作一个整体来处理。

3、结构体类型定义末尾括号后的分号不可缺少。

4、结构体类型的成员可以是一个结构体变量,但不能是自身结构体类型的变量。

结构体变量的定义

上个小节只是定义了结构体类型,它仅相当于一个模型,其中并无具体数据,系统也不会为它分配内存空间。

为了能在程序中使用结构体类型的数据,应该定义结构体类型的变量,并在其中存放具体的数据。

下列是定义结构体变量的三种方式。

1、先定义结构体类型,再定义结构体变量

定义好结构体类型后,就可以定义结构体变量了,定义结构体变量的语法格式如下所示:

struct结构体类型名结构体变量名;

例如:

structstudentstu1,stu2;

上述示例定义了结构体类型变量stu1和stu2,这时变量stu1和stu2具有了结构体特征,它们各自存储了一组基本类型的变量,具体如图9-2所示。

图9-2变量stu1、stu2的存储结构

从图9-2中可以看出,变量stu1和stu2分别占据了一块连续的内存空间。

2、在定义结构体类型的同时定义结构体变量

该方式的作用与第一种方式相同,其语法格式如下所示:

struct结构体类型名称

{

数据类型成员名1;

数据类型成员名2;

……

数据类型成员名n;

}结构体变量名列表;

例如:

structstudent

{

intnum;

charname[10];

charsex;

}stu1,stu2;

上述代码在定义结构体类型student的同时,也定义了结构体类型变量stu1和stu2。

其中变量stu1和stu2中包含的成员的数据类型都是一样的。

3、直接定义结构体变量

除了上述两种方式外,我们还可以直接定义结构体变量,其语法格如下所示:

struct

{

数据类型成员名1;

数据类型成员名2;

数据类型成员名n;

}结构体变量名列表;

例如:

struct

{

intnum;

charname[10];

charsex;

}stu1,stu2;

上述代码同样定义了结构体变量stu1和stu2,但采用这种方式定义的结构体是没有类型名称,我们称之为匿名结构体。

注意:

结构体类型是用户自定义的一种数据类型,它同前面所介绍的简单数据类型一样,在编译时对结构体类型不分配空间。

只有用它来定义某个变量时,才会为该结构体变量分配结构体类型所需大小的内存单元。

结构体变量的内存分配

结构体变量一旦被定义,系统就会为其分配内存。

结构体变量占据的内存大小是按照字节对齐的机制来分配的。

字节对齐就是字节按照一定规则在空间上排列。

通常情况下,字节对齐满足两个原则,具体如下:

(1)结构体的每个成员变量相对于结构体首地址的偏移量,是该成员变量的基本数据类型(不包括结构体、数组等)大小的整数倍,如果不够,编译器会在成员之间加上填充字节。

接下来通过一个案例来打印出每个成员变量的地址,如例9-1所示。

例9-1

1#include

2//直接定义结构体变量

3struct

4{

5chara;

6doubleb;

7intc;

8shortd;

9}s;

10voidmain()

11{

12printf("成员变量a的地址:

%d\n",&s.a);

13printf("成员变量b的地址:

%d\n",&s.b);

14printf("成员变量c的地址:

%d\n",&s.c);

15printf("成员变量d的地址:

%d\n",&s.d);

16printf("成员变量d的地址:

%d\n",&s+1};

17}

运行结果如图9-3所示。

图9-3运行结果

从图9-3中可以看出,结构体变量中成员变量的地址都被打印出来了。

结构体变量S中各成员在内存中所占内存如图9-4所示。

图9-4结构体变量中各成员所占内存图

接下来根据图9-3和图9-4来逐步分析每个成员变量的地址:

●成员变量a的地址是3703968,同时这也是结构体变量s的首地址;

●成员变量b的地址是3703976,相对于结构体变量的首地址的偏移量是8个字节。

这是因为成员变量b的基本数据类型是double型,其偏移量应该是8(sizeof(double))的倍数,所以a变量后面被填充了7个字节;

●成员变量c的地址是3703984,相对于结构体变量的首地址的偏移量是16,是变量a与变量b所占内存大小之和,正好也是4(sizeof(int))的倍数;

●成员变量d的地址是3703988,相对于首地址的偏移量是20字节,是变量a、b、c所占内存大小之和,正好也是2(sizeof(short))的倍数。

(2)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如果不够,编译器会在最末一个成员之后加上填充字节。

经过例9-1的分析,可以计算出结构体变量s的内存大小是22,但是这并不符合字节对齐的第二项准则,下面将例9-1中的结构体变量在内存中的大小打印出来,如例9-2所示。

例9-2

18#include

19struct

20{

21chara;

22doubleb;

23intc;

24shortd;

25}s;

26voidmain()

27{

28printf("结构体变量s在内存中的大小:

%d\n",sizeof(s));

29}

运行结果如图9-5所示。

图9-5运行结果

从图9-5中可以看出,结构体变量s的内存大小为24字节,而非22字节。

这是因为,结构体的总大小应该是最宽基本类型成员大小的整数倍。

成员变量中最宽基本类型的大小为8(sizeof(double)),所以d后面被填充了2个字节,结构体变量s所占内存大小为24字节。

其内存分配如图9-6所示。

图9-6结构体s占内存的总大小

需要注意的是,如果结构体中有构造类型变量,如结构体中有char类型数组成员,则其偏移量是按数组中的元素类型为基准,即偏移量是1(sizeof(char))的倍数。

如果是int类型数组,则偏移量是4(sizeof(int))的倍数。

关于结构体变量的分配,不同的编绎器有不同的分配规则,读者了解即可,在实际应用中可用sizeof()运算符很快捷的求出结构体变量的大小。

结构体变量的初始化

由于结构体变量中存储的是一组类型不同的数据,因此,为结构体变量初始化的过程,其实就是为结构体中各个成员初始化的过程。

根据结构体变量定义方式的不同,结构体变量初始化的方式可分为两种。

1、在定义结构体类型和结构体变量的同时,对结构体变量初始化,具体示例如下:

structStudent

{

intnum;

charname[10];

charsex;

}stu={20140101,"ZhangSan",'M'};

上述代码在定义结构体变量stu的同时,就对其中的成员进行了初始化。

2、定义好结构体类型后,对结构体变量初始化,具体示例如下:

structStudent

{

intnum;

charname[10];

charsex;

};

structStudentstu={20140101,"ZhangSan",'M'};

在上述代码中,首先定义了一个结构体类型Student,然后在定义结构体变量时,为其中的成员进行初始化。

脚下留心:

初始化一部分成员变量

在对结构体初始化时,如果只初始化其中一部分成员,则要对前面的成员初始化,后面的成员可以空余,因为给成员变量赋值时,编绎器是从按成员从前往后匹配,而不是按数据类型自动去匹配。

可以给一部分成员初始化,然后将各个成员打印出来,如例9-3所示。

例9-3

#include

structStudent

{

intnum;

charname[10];

charsex;

}stu={20140101,'M'};

voidmain()

{

printf("num=%d\n",stu.num);

printf("name=%s\n",stu.name);

printf("sex=%c\n",stu.sex);

}

运行结果如图9-7所示。

图9-7运行结果

如图9-7所示,在结构体变量stu中只对num和sex赋值,但在输出所有成员的值时,’M’却赋值给了name,sex没有值。

结构体变量的引用

定义并初始化结构体变量的目的是使用结构体变量中的成员。

在C语言中,引用结构体变量中一个成员的方式如下所示:

结构体变量名.成员名;

例如,下列的语句用于引用结构体变量stu1中num成员:

stu1.num;

为了帮助大家更好地掌握结构体变量的使用,接下来通过一个案例来输出结构体变量中所有成员的值,如例9-4所示。

例9-4

30#include

31#include

32structStudent

33{

34charname[50];

35intage;

36};

37voidmain()

38{

39structStudents={"ZhangSan",23};

40s.age=24;

41printf("%s%d\n",s.name,s.age);

42}

运行结果如图9-8所示。

图9-8运行结果

从图9-8中可以看出,结构体变量中成员name和age的值被输出了。

结构体数组

一个结构体变量可以存储一组数据,例如一个学生的序号、姓名、性别等数据。

如果有10个学生的信息需要存储,可以采用结构体数组。

与前面讲解的数组不同,结构体数组中的每个元素都是结构体类型的,它们都是具有若干个成员的项。

本节将针对结构体数组的定义、引用及初始化方式进行详细地讲解。

结构体数组的定义

假设一个班有20个学生,如果我们需要描述这20个学生的信息,可以定义一个长度为20的Student类型数组,与定义结构体变量一样,可以采用三种方式定义结构体数组stus。

1、先定义结构体类型,后定义结构体数组,具体示例如下:

structStudent

{

intnum;

charname[10];

charsex;

};

structStudentstus[20];

2、在定义结构体类型的同时定义结构体数组,具体示例如下:

structStudent

{

intnum;

charname[10];

charsex;

}stus[20];

3、直接定义结构体数组,具体示例如下:

struct

{

intnum;

charname[10];

charsex;

}stus[20];

结构体数组的初始化

结构体数组的初始化方式与数组类似,都是通过为元素赋值的方式完成的。

由于结构体数组中的每个元素都是一个结构体变量,因此,在为每个元素赋值的时候,需要将其成员的值依次放到一对大括号中。

例如,定义一个结构体数组students,该数组有3个元素,并且每个元素有num,name,sex三个成员,可以采用下列两种方式对结构体数组students初始化。

1、先定义结构体数组类型,然后初始化结构体数组,具体示例如下:

structStudent

{

intnum;

charname[10];

charsex;

};

structStudentstudents[3]={{20140101,"ZhangSan",'M'},

{20140102,"LiSi",'W'}

{20140103,"ZhaoLiu",'M'}

};

2、在定义结构体数组的同时,对结构体数组初始化,具体示例如下:

structStudent

{

intnum;

charname[10];

charsex;

}students[3]={{20140101,"ZhangSan",'M'},

{20140102,"LiSi",'W'},

{20140103,"ZhaoLiu",'M'}

};

当然,使用这种方式初始化结构体数组时,也可以不指定结构体数组的长度,系统在编译时,会自动根据初始化的值决定结构体数组的长度。

例如,下列初始化方式也是合法的。

structStudent

{

intnum;

charname[10];

charsex;

}Studentstudents[]={{20140101,"ZhangSan",'M'},

{20140102,"LiSi",'W'},

{20140103,"ZhaoLiu",'M'}

};

结构体数组的引用

结构体数组的引用是指对结构体数组元素的引用,由于每个结构体数组元素都是一个结构体变量,因此,结构体数组元素的引用方式与结构体变量类似,其语法格式如下所示:

数组元素名称.成员名;

例如,要引用9.2.2小节中结构体数组student第一个元素的num成员,可以采用下列方式:

student[0].num;

为了帮助读者更好地掌握结构体数组的引用,接下来通过一个案例来输出结构体数组中的所有成员,如例9-5所示。

例9-5

43#include

44#include

45structStudent

46{

47charname[50];

48intstudentID;

49};

50voidmain()

51{

52structStudents[2]={{"ZhangSan",20140000},{"LiSi",20140001}};

53for(inti=0;i<2;i++)

54{

55printf("%s%d\n",s[i].name,s[i].studentID);

56}

57}

运行结果如图9-9所示。

图9-9运行结果

在例9-5中,首先定义了一个长度为2的结构体数组s,并对数组中的元素进行了初始化。

然后使用for循环,依次输出了s[0]和s[1]中的成员值。

结构体指针变量

在第6章学习指针时,指针指向的都是基本数据类型。

其实,指针还可以指向结构体,被称为结构体指针变量,它的用法与一般指针没有太大差异,本节将围绕结构体指针变量进行详细讲解。

结构体指针变量

在使用结构体指针变量之前,首先需要定义结构体指针,结构体指针的定义方式与一般指针类似,例如,下列语句定义了一个Student类型的指针。

structStudents={"ZhangSan",20140100,'M',93.5};

structStudent*p=&s;

在上述代码中,定义了一个结构体指针p,并通过“&”将结构体变量s的地址赋值给p,因此,p就是指向结构体变量s的指针。

当程序中定义了一个指向结构体变量的指针后,就可以通过“指针名成员变量名”的方式来访问结构体变量中的成员,接下来通过一个案例来演示结构体指针的用法,如例9-6所示。

例9-6

58#include

59#include

60structStudent

61{

62charname[50];

63intstudentID;

64};

65voidmain()

66{

67structStudents={"ZhangSan",20140000};

68structStudent*p=&s;

69printf("%s%d\n",p->name,p->studentID);

70}

运行结果如图9-10所示。

图9-10运行结果

在例9-6中,首先定义了一个结构体类型变量s,并将变量s中的成员name初始化为ZhangSan,studentID初始化为20140000。

然后,定义了一个结构体指针p,并将p指向s的地址,最后,通过p->name访问成员name和studentID的值。

从图9-10中可以看出,使用结构体指针同样可以访问结构体变量中的成员。

结构体数组指针

指针可以指向结构体数组,即将结构体数组的起始地址赋给指针变量,这种指针就是结构体数组指针,例如,下面语句定义了Student结构体的一个数组和该数组的指针。

structStudentstu1[10],*p=&stu1;

在上述代码中,p是一个Student结构体数组指针,从定义上看,它和结构体指针没什么区别,只不过指向的是结构体数组。

为了帮助读者更好地掌握结构体数组指针的用法,接下来通过一个案例来演示如何使用结构体数组指针输出多个学生的信息,如例9-7所示。

例9-7

71#include

72#include

73structstudent

74{

75intnum;

76charname[20];

77charsex;

78intage;

79}stu[3]={

80{201401001,"WangMing",'M',19},

81{201401002,"ZhangNing",'W',23},

82{201401003,"WangMing",'M',19}};

83voidmain()

84{

85structstudent*p;

86printf("num\t\tname\t\tsex\tage\n");

87for(p=stu;p

88{

89printf("%ld\t%-12s\t%-2c\t%4d\n",p->num,p->name,p->sex,p->age);

90}

91}

运行结果如图9-11所示。

图9-11运行结果

在例9-7中,第3~12行代码用于初始化结构体数组stu,在第17行代码中,“p=stu”用于将p指向结构体数组stu的第一个元素,“p++”用于将指针指向下一个元素。

第19行代码,通过“->”用于获取某个成员的值。

从图9-11中可以看出,程序输出了结构体数组stu中所有元素的成员值。

结构体类型数据在函数间的传递

在函数间不仅可以传递简单的变量、数组、指针等类型的数据,还可以传递结构体类型的数据。

本节将针对结构体类型数据在函数间的传递进行详细讲解。

结构体变量作为函数参数

结构体变量作为函数参数的用法与普通变量类似,都需要保证调用函数的实参类型和被调用函数的形参类型相同。

结构体变量作函数参数时,也是值传递,被调函数中改变结构体成员变量的值,主调函数中不受影响。

如例9-8所示

例9-8

92#include

93structStudent

94{

95charname[50];

96intstudentID;

97};

98voidchange(structStudentstu)

99{

100strcpy(stu->name,"lisi");

101stu.studentID=2;

102}

103voidmain()

104{

105structStudentstudent={"ZhangSan",1

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

当前位置:首页 > 教学研究 > 教学计划

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

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