第9章 结构体和共用体12-07

1970-01-01 08:00

博学谷——让IT教学更简单,让IT学习更有效

第9章 结构体和共用体

学习目标

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

? 掌握结构体与数组、指针、函数等结合使用 ? 掌握共用体变量的定义与引用 ? 理解结构体与共用体的内存分配机制

前面章节所学的数据类型都是分散的、互相独立的,例如定义int a和char b两个变量,这两个变量是毫无内在联系的,但在实际生活和工作中,经常需要处理一些关系密切的数据,例如,描述公司一个员工的姓名、部门、职位、电话、E-mail地址等,由于这些数据的类型各不相同,因此,要想对这些数据进行统一管理,仅靠前面所学的基本类型和数组都很难实现。为此,C语言提供了另外两种构造类型,分别是结构体和共用体,本章将围绕这两种构造类型进行详细地讲解。

9.1 结构体类型和结构体变量

9.1.1 结构体类型定义

结构体是一种构造数据类型,把不同类型的数据整合在一起,每一个数据都称为该结构体类型的成员。在程序设计中,使用结构体类型时,首先要对结构体类型的组成进行描述,结构体类型的定义方式如下所示:

struct 结构体类型名称 { };

数据类型 成员名1; 数据类型 成员名2; ……

数据类型 成员名n;

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

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

1

博学谷——让IT教学更简单,让IT学习更有效

struct Student { };

int num; char name[10]; char sex; int age;

char address[30];

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

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

struct Date { };

struct Student {

int num; char name[10]; char sex;

struct Date birthday; int year; int month; int day;

} stu1;

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

numnumnamenamesexsexyearyearbirthdaybirthdaymonthmonthdayday

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

注意:

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

变量名的命名规则相同。

2、 定义好一个结构体类型后,并不意味着立即分配一块内存单元来存放各个数据成员,它只是

告诉编译器,该结构体类型是由哪些数据类型的成员构成,各占多少个字节,按什么格式存储,并把它们当作一个整体来处理。

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

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

9.1.2 结构体变量的定义

上个小节只是定义了结构体类型,它仅相当于一个模型,其中并无具体数据,系统也不会为它分配内存空间。为了能在程序中使用结构体类型的数据,应该定义结构体类型的变量,并在其中存 2

博学谷——让IT教学更简单,让IT学习更有效 放具体的数据。下列是定义结构体变量的三种方式。

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

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

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

例如:

struct student stu1, stu2;

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

stu1:stu2:201403001201403001Zhang SanZhang San201403002201403002Li YunLi YunManManWomanWoman23232525BeijingBeijingShanghaiShanghai

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

从图9-2中可以看出,变量stu1和stu2分别占据了一块连续的内存空间。 2、 在定义结构体类型的同时定义结构体变量

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

struct 结构体类型名称 {

数据类型 成员名1; 数据类型 成员名2; ……

数据类型 成员名n;

} 结构体变量名列表;

例如:

struct student {

int num; char name[10]; char sex;

} stu1, stu2;

上述代码在定义结构体类型student的同时,也定义了结构体类型变量stu1和stu2。其中变量stu1和stu2中包含的成员的数据类型都是一样的。

3、 直接定义结构体变量

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

struct {

数据类型 成员名1; 数据类型 成员名2; …

数据类型 成员名n;

} 结构体变量名列表;

例如:

3

博学谷——让IT教学更简单,让IT学习更有效

struct {

int num; char name[10]; char sex;

} stu1, stu2;

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

注意:

结构体类型是用户自定义的一种数据类型,它同前面所介绍的简单数据类型一样,在编译时对结构体类型不分配空间。只有用它来定义某个变量时,才会为该结构体变量分配结构体类型所需大小的内存单元。

9.1.3 结构体变量的内存分配

结构体变量一旦被定义,系统就会为其分配内存。结构体变量占据的内存大小是按照字节对齐的机制来分配的。字节对齐就是字节按照一定规则在空间上排列。通常情况下,字节对齐满足两个原则,具体如下:

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

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

1 #include 2 // 直接定义结构体变量 3 struct 4 { 5 6 7 8

char a; double b; int c; short d;

9 } s;

10 void main() 11 { 12 13 14 15 17 }

printf(\成员变量a的地址:%d\\n\printf(\成员变量b的地址:%d\\n\printf(\成员变量c的地址:%d\\n\printf(\成员变量d的地址:%d\\n\

16 printf(\成员变量d的地址:%d\\n\

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

4

博学谷——让IT教学更简单,让IT学习更有效

图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

1 #include 2 struct 3 { 4 5 6 7

char a; double b; int c; short d;

8 } s;

9 void main() 10 { 11 12 }

printf(\结构体变量s在内存中的大小:%d\\n\

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

5

博学谷——让IT教学更简单,让IT学习更有效

图9-5 运行结果

从图9-5中可以看出,结构体变量s的内存大小为24字节,而非22字节。这是因为,结构体的总大小应该是最宽基本类型成员大小的整数倍。成员变量中最宽基本类型的大小为8(sizeof(double)),所以d后面被填充了2个字节,结构体变量s所占内存大小为24字节。其内存分配如图9-6所示。

占1个字节填充7个字节s成员成员aa占8个字节成员成员bb占4个字节成员成员cc占2个字节填充2个字节成员成员dd

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

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

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

9.1.4 结构体变量的初始化

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

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

struct Student {

int num; char name[10]; char sex;

}stu={20140101,\

上述代码在定义结构体变量stu的同时,就对其中的成员进行了初始化。 2、定义好结构体类型后,对结构体变量初始化,具体示例如下:

struct Student { };

struct Student stu = {20140101,\

int num; char name[10]; char sex;

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

6

博学谷——让IT教学更简单,让IT学习更有效

?脚下留心:初始化一部分成员变量

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

例9-3

#include struct Student {

int num; char name[10]; char sex;

}stu = { 20140101, 'M' }; void main() {

printf(\

printf(\printf(\}

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

图9-7 运行结果

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

9.1.5 结构体变量的引用

定义并初始化结构体变量的目的是使用结构体变量中的成员。在C语言中,引用结构体变量中一个成员的方式如下所示:

结构体变量名.成员名;

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

stu1.num;

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

例9-4

1 #include 2 #include 3 struct Student 4 {

7

博学谷——让IT教学更简单,让IT学习更有效

5 6 7 };

8 void main() 9 { 10 12 13 }

struct Student s = {\printf(\

11 s.age = 24;

char name[50]; int age;

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

图9-8 运行结果

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

9.2 结构体数组

一个结构体变量可以存储一组数据,例如一个学生的序号、姓名、性别等数据。如果有10个学生的信息需要存储,可以采用结构体数组。与前面讲解的数组不同,结构体数组中的每个元素都是结构体类型的,它们都是具有若干个成员的项。本节将针对结构体数组的定义、引用及初始化方式进行详细地讲解。

9.2.1 结构体数组的定义

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

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

struct Student { };

struct Student stus[20];

int num; char name[10]; char sex;

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

struct Student {

int num; char name[10]; char sex;

} stus[20];

8

博学谷——让IT教学更简单,让IT学习更有效

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

struct {

int num; char name[10]; char sex;

} stus[20];

9.2.2 结构体数组的初始化

结构体数组的初始化方式与数组类似,都是通过为元素赋值的方式完成的。由于结构体数组中的每个元素都是一个结构体变量,因此,在为每个元素赋值的时候,需要将其成员的值依次放到一对大括号中。

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

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

struct Student { };

struct Student students[3] = { {20140101, \

{20140102, \ {20140103, \};

int num; char name[10]; char sex;

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

struct Student {

int num; char name[10]; char sex;

{20140102, \{20140103, \};

} students[3] = { {20140101, \

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

struct Student {

int num; char name[10]; char sex;

9

博学谷——让IT教学更简单,让IT学习更有效

}Student students[] = { {20140101, \

{20140102, \{20140103, \};

9.2.3 结构体数组的引用

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

数组元素名称.成员名;

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

student[0].num;

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

例9-5

1 #include 2 #include 3 struct Student 4 { 5 6 7 };

8 void main() 9 { 10 11 12 13 14 15 }

struct Student s[2] = {{\for (int i = 0; i < 2; i++) { }

printf(\char name[50]; int studentID;

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

图9-9 运行结果

在例9-5中,首先定义了一个长度为2的结构体数组s,并对数组中的元素进行了初始化。然后使用for循环,依次输出了s[0]和s[1]中的成员值。

9.3 结构体指针变量

在第6章学习指针时,指针指向的都是基本数据类型。其实,指针还可以指向结构体,被称为结构体指针变量,它的用法与一般指针没有太大差异,本节将围绕结构体指针变量进行详细讲解。

10

博学谷——让IT教学更简单,让IT学习更有效

9.3.1 结构体指针变量

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

struct Student s = {\struct Student *p = &s;

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

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

例9-6

1 #include 2 #include 3 struct Student 4 { 5 6 7 };

8 void main() 9 { 10 11 12 13 }

struct Student s = {\struct Student *p = &s;

printf(\char name[50]; int studentID;

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

图9-10 运行结果

在例9-6中,首先定义了一个结构体类型变量s,并将变量s中的成员name初始化为Zhang San,studentID 初始化为20140000。然后,定义了一个结构体指针p,并将p指向s的地址,最后,通过p->name访问成员name和studentID的值。从图9-10中可以看出,使用结构体指针同样可以访问结构体变量中的成员。

9.3.2 结构体数组指针

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

struct Student stu1[10],*p=&stu1;

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

为了帮助读者更好地掌握结构体数组指针的用法,接下来通过一个案例来演示如何使用结构体

11

博学谷——让IT教学更简单,让IT学习更有效 数组指针输出多个学生的信息,如例9-7所示。

例9-7

1 #include 2 #include 3 struct student 4 {

5 int num; 6 char name[20]; 7 char sex; 8 int age; 9 } stu[3]={

10 {201401001,\ 11 {201401002,\ 12 {201401003,\ 13 void main() 14 {

15 struct student *p;

16 printf(\ 17 for(p=stu;p

19 printf(\ 20 } 21 }

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

图9-11 运行结果

在例9-7中,第3~12行代码用于初始化结构体数组stu,在第17行代码中,“p=stu”用于将p指向结构体数组stu的第一个元素,“p++”用于将指针指向下一个元素。第19行代码,通过“->”用于获取某个成员的值。从图9-11中可以看出,程序输出了结构体数组stu中所有元素的成员值。

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

在函数间不仅可以传递简单的变量、数组、指针等类型的数据,还可以传递结构体类型的数据。本节将针对结构体类型数据在函数间的传递进行详细讲解。

9.4.1 结构体变量作为函数参数

结构体变量作为函数参数的用法与普通变量类似,都需要保证调用函数的实参类型和被调用函数的形参类型相同。结构体变量作函数参数时,也是值传递,被调函数中改变结构体成员变量的值,

12

博学谷——让IT教学更简单,让IT学习更有效 主调函数中不受影响。如例9-8所示

例9-8

1 #include 2 struct Student 3 { 4 5 6 };

7 void change(struct Student stu) 8 { 9 10 11 }

12 void main() 13 { 14 15 16 17 }

struct Student student = { \change(student);

printf(\strcpy(stu->name, \stu.studentID = 2; char name[50]; int studentID;

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

图9-12 运行结果

在例9-8中,定义了一个用于改变数据的change()函数,该函数需要接收一个结构体类型的参数。从代码第15行可以看出,当将结构体变量作为参数传递给函数时,其传参的方式与普通变量相同。在主调函数中调用change()函数后,主函数中结构体中成员的值并没有改变。

9.4.2 结构体数组作为函数参数

函数间不仅可以传递一般的结构体变量,还可以传递结构体数组。接下来通过一个案例来演示如何使用结构体数组作为函数参数传递数据,如例9-9所示。

例9-9

1 #include 2 struct Student 3 { 4 5 6 };

7 void printInfo(struct Student stu[],int length) 8 { 9

for (int i = 0; i < length; i++) char name[50]; int studentID;

13

博学谷——让IT教学更简单,让IT学习更有效

10 11 12 13 14 }

15 void main() 16 { 17

struct Student students[3] = { { \

18 {\ 19 {\ 20 21 }

printInfo(students, 3); { }

printf(\printf(\

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

图9-13 运行结果

在例9-9中,由于无法通过数组直接获取到其长度,因此,在定义的printfInfo()函数中,需要传递两个参数,其中一个是结构体数组,另一个是数组的长度。printfInfo()函数接收到传递来的数组名和长度后,使用for循环,将结构体数组中的所有成员输出。

9.4.3 结构体指针作为函数参数

结构体指针变量用于存放结构体变量的首地址,所以将指针作为函数参数传递时,其实就是传递结构体变量的首地址,在被调函数中改变结构体成员的值,那么主调函数中结构体成员的值也会被改变。如例9-10所示。

例9-10

1 #include 2 struct Student 3 { 4 5 6 };

7 void change(struct Student* stu) 8 { 9 10 11 }

12 void main()

strcpy(stu->name, \stu->studentID = 2; char name[50]; int studentID;

14

博学谷——让IT教学更简单,让IT学习更有效

13 { 14 15 16 17 }

struct Student student = { \change(&student);

printf(\

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

图9-14 运行结果

在例9-10中,定义了一个改变数据的change()函数,该函数需要接收一个结构体指针类型的参数,由于结构体指针作为函数参数时,需要传递的是结构体变量的首地址,因此,在代码第15行中,通过“&”运算符获取结构体变量student的首地址,然后将其作为参数传递给change()函数。在change()函数中改变结构体变量成员的值,则main()函数中的结构体成员值也随着改变了。

9.5 union共用体

共用体又叫联合体,是一种特殊的数据类型,它允许多个成员使用同一块内存。灵活地使用共用体可以减少程序所使用的内存。本节将针对共用体进行详细地讲解。

9.5.1 共用体数据类型的定义

在C语言中,共用体类型同结构体类型一样,都属于构造类型,它在定义上与结构体类型十分相似,定义共用体类型的语法格式如下所示:

union 共用体类型名称 {

数据类型 成员名1; 数据类型 成员名2;

……

数据类型 成员名n; };

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

例如下面这段代码:

union data {

int m; float x; char c; };

15

博学谷——让IT教学更简单,让IT学习更有效

上述代码定义了一个名为data的共用体类型,该类型由三个不同类型的成员组成,这些成员共享同一块存储空间。

9.5.2 共用体变量的定义

共用体变量的定义和结构体变量的定义类似,假如要定义两个data类型的共用体变量a和b,则可以采用下列三种方式。

1、先定义共用体类型,再定义共用体变量,具体示例如下:

union data {

int m;

float x; char c; };

union data a,b;

2、在定义共用体类型的同时定义共用体变量,具体示例如下:

union data {

int m; float x; char c; }a,b;

3、直接定义共用体类型变量,具体示例如下:

union {

int m; Double x; char c; }a,b;

上述三种方式都用于定义共用体变量a和b, 和结构体变量的定义相同。

?多学一招:共用体内存分配

共用体的内存分配必须要符合两项准则,具体如下:

1、 共用体的内存必须大于或等于其成员变量中大数据类型(包括基本数据类型和数组)的大

2、共用体的内存必须是最宽基本数据类型的整数倍,如果不是,则填充字节。接下来通过两个共用体的内存分析来解释上述准则。

(1)成员变量都是基本数据类型的共用体,具体如下

union {

int m; float x; char c; }a;

16

博学谷——让IT教学更简单,让IT学习更有效

共用体a的内存大小如图9-15所示。

结构体变量首地址最大基本数据类型float大小:4个字节

图9-15 共用体a的内存大小

共用体a的内存大小是最大数据类型所占的字节数,即int和float的大小,所以共用体a的内存大小为4字节。

(2)成员变量包含数组类型的共用体,具体如下

union {

int m; float x; char c; char name[5]; }b;

共用体b的内存大小如图9-16所示。

结构体变量首地址Char name[5]所占字节大小:5个字节3个填充字节

图9-16 共用体b的内存大小

共用体b的内存大小是按最大数据类型char name[5]来分配, char name[5]占5个字节。共用体b的内存大小还必须是最宽基本数据类型的整数倍,所以填充3个字节,共8个字节。关于共用体变量的内存大小,读者可以通过sizeof()来验证。

9.5.3 共用体变量的初始化和引用

在共用体变量定义的同时,只能对其中一个成员的类型值进行初始化,这与它的内存分配也是相应的。共用体变量初始化的方式如下所示:

union 共用体类型名 共用体变量={其中一个成员的类型值}

从上述语法格式可以看出,尽管只能给其中一个成员赋值,但必须用大括号括起来。 例如,下列语句用于对data类型的共用体变量a进行初始化。

17

博学谷——让IT教学更简单,让IT学习更有效

union data a={8};

完成了共用体变量的初始化后,就可以引用共用体中的成员了,共用体变量的引用方式与结构体类似,例如,下列代码定义了一个共用体变量a和一个共用体指针p。

union data {

int m; float x; char c; };

union data a, *p=&a;

如果要引用共用体变量中的m成员,则可以使用下列方式:

a.m; // 引用共用体变量a中的成员m

p->m // 引用共用体指针变量p所指向的变量成员m

需要注意的是,虽然共用体变量的引用方式与结构体类似,但两者是有区别的,其主要区别是,在程序执行的任何特定时刻,结构体变量中的所有成员是同时驻留在该结构体变量所占用的内存空间中,而共用体变量仅有一个成员驻留在共用体变量所占用的内存空间中。接下来通过一个案例来验证,如例9-11所示。

例9-11

1 #include 2 union Data 3 { 4 5 6 };

7 void main() 8 { 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 }

printf(\的地址%p\\n\printf(\的地址%p\\n\printf(\的地址%p\\n\

union Data d; // 定义了一个union d.i = 15;

printf(\printf(\printf(\

d.j = 'a';

printf(\printf(\printf(\int i; char j;

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

18

博学谷——让IT教学更简单,让IT学习更有效

图9-17 运行结果

在例9-11中,定义了一个Data类型的共用体变量d,其中包含了两个int类型的变量i和char类型的变量j,当将i赋值为15时,则此时结构体d中只有一个值15,取i的值、j的值和结构体变量d的值,其实都是取对应内存中的同一个值。当将j的值为字符a时,则此时d对应的内存中只有字符a,取i,j,d的值都是97。最后打印d、i、j的地址,三者的地址是相同的,更进一步说明共用体变量在任何时该都只有一个成员值在内存中。

9.6 Typedef——给数据类型取别名

在前面章节中,讲解了C语言提供的各种数据类型和用户自己声明的结构体、共用体、指针类型等。除了这些数据类型,C语言还允许用户使用typedef关键字为现有数据类型取别名。使用typedef关键字可以方便程序的移植,减少对硬件的依赖性。接下来将针对typedef关键字进行详细地讲解。

使用typedef关键字语法格式如下:

typedef 数据类型 别名;

在上述语法格式中,数据类型包括基本数据类型、构造数据类型、指针等,接下来针对这几项进行详细讲解。

1、为基本类型取别名

使用typedef关键字为int类型取别名,示例代码如下:

typedef int ZX; ZX i,j,k;

上面的语句将int数据类型定义成ZX,则在程序中可以用ZX定义整型变量。 2、为数组类型取别名

使用typedef关键字为数组取别名,示例代码如下:

typedef char NAME[10]; NAME class1,class2;

上面的语句定义了一个可含有10个字符的字符数组名NAME,并用NAME定义了两个字符数组class1和class2,等效于char class1[10]和char class2[10]。

3、为结构体取别名

使用typedef关键字为结构体类型Student取别名,示例代码如下:

typedef struct Student {

int num; char name[10];

19

博学谷——让IT教学更简单,让IT学习更有效

char sex; }STU; STU stu1;

上面的语句定义了一个Student类型的结构体STU,STU stu1;语句等效于struct Student stu1;语 句。

需要注意的是,使用typedef关键字只是对已存在的类型取别名,而不是定义了新的类型。有时也可以用宏定义来代替typedef的功能,但是宏定义是由预处理完成的,而typedef是在编译时完成的,使用typedef更加灵活。

9.7 进阶案例——求学生平均成绩

学习完了结构体与共用体的相关知识,接下来通过一个计算平均成绩的案例来加深对结构体的理解,具体需求如下:

? 一个小组中有3个学生,每个学生修4门课程(三个学生修的课程相同)

? 在程序中输入这3个同学的4门课程成绩,程序能自动计算出这4门课程的小组平均成绩 接下来用代码来实现上述功能,如例9-12所示。 例9-12

1 #define _CRT_SECURE_NO_WARNINGS 2 #include 3 #include 4

5 typedef struct stu{ 6 7 9

10 void main() 11 { 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

//将3个学生的每科成绩相加,然后求平均成绩 for (j = 0; j < 4; j++) {

int i, j;

float sum = 0, avg;

STU stu[3]; //定义一个结构体数组,数组中有3个元素

//从键盘输入学生姓名及成绩 for (i = 0; i < 3; i++) { }

printf(\请输入第%d个学生的姓名及四门课成绩:\\n\scanf(\for (j = 0; j < 4; j++)

scanf(\

char name[20]; //学生姓名 float score[4]; //4门课成绩

8 }STU;

20

博学谷——让IT教学更简单,让IT学习更有效

28 29 30 31 32 33 34 35 36 37 }

}

system(\

for (i = 0; i < 3; i++) { }

avg = sum / 3;

sum = 0; //注意:sum归0

printf(\第%d门学科平均成绩为:%.2f\\n\

sum = sum + stu[i].score[j];

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

图9-18 运行结果

如图9-18所示,程序实现了输入学生姓名、成绩并计算出每门学科平均成绩的功能。在例9-12中,首先定义了一个结构体类型stu,并定义该结构体类型数组stu[3]用于存储学生姓名和成绩。然后用scanf()实现输入学生姓名和成绩的功能。最后将3个学生的各科科成绩分别相加,然后求小组中每门课程的平均成绩并打印出来。

9.8 本章小结

本章主要讲解了结构体和共用体两种构造类型,其中,结构体允许将若干个相关的、数据类型不同的数据作为一个整体处理,并且每个数据各自分配了不同的内存空间,而共用体中所有的成员共享同一段内存空间。通过本章的学习,希望大家熟练掌握结构体和共用体的定义、初始化以及引用方式,为后期复杂数据的处理提供有力的支持。

21

博学谷——让IT教学更简单,让IT学习更有效

在学习本章知识的过程中,初学者可能会遇到各种各样的问题,例如“结构体如何分配内存”、“结构体和共用体的区别”、“typedef有什么用”等。针对上述问题初学者可以扫描右侧二维码关注问答精灵,让问答精灵帮你解决学习中的困难。

9.9 习题

一、填空题 1、在C语言中,结构体类型和共用体类型都属于_____类型。

2、定义结构体类型的关键字是________。 3、定义共用体类型的关键字是________。 4、引用结构体变量stu中num成员的方式是________。

5、在结构体变量a中,定义了一个int类型的成员和一个float类型的成员,那么,系统为变量a分配的内存是_____字节。

二、判断题

1、 结构体类型是由不同类型的数据组成的。

2、 若已知指向结构体变量stu的指针p,在引用结构体成员时,有三种等价的形式,即stu.成

员名、*p.成员名、p->成员名。

3、 使几个不同类型的变量共占同一段内存的结构称为共用体。

4、 在定义一个共用体变量时,系统分配给它的存储空间是该共用体中占有最大存储空间的成

员所需的存储空间。 5、 已知共用体

union u {

int a; char c; float f; }

其各个成员地址&u.a、&u.c、&u.f是不同的。 三、选择题

1、 在C语言中,系统为一个结构体变量分配的内存是?

A、各成员所需内存量的总和

B、结构体第一个成员所需的内存量 C、成员中占内存量最大者所需的容量 D、结构体中最后一个成员所需的内存量

2、 下列关于共用体类型变量的描述中,正确的是?

A、可以对共用体变量直接赋值

B、一个共用体变量可以同时存放其所有的成员 C、一个共用体变量中不可以同时存放其所有的成员 D、共用体类型定义中,不能出现结构体类型的成员

22

博学谷——让IT教学更简单,让IT学习更有效

3、 设有如下定义:

struct sk{int a; float b;}data,*p;

若有p=&a; 则对data中成员a的引用正确的是?

A、(*p).data.a B、(*p).a C、p->data.a D、p.data.a

4、 C语言中,共用体类型变量在程序执行期间?

A、所有成员已知驻留在内存中 B、只有一个成员驻留在内存中 C、部分成员驻留在内存中 D、没有成员驻留在内存中 5、 阅读下列程序

main(){

struct cmplx

{

int x; int y;

}com[2]={1,3,2,7};

printf(“%d\\n”,com[0].y/com[0].x*com[1].x); }

程序的输出结果为?

A、 0 B、1 C、3 D、6

四、简答题

1、简述结构体类型和共用体类型的定义方式。 2、简述结构体变量和共用体变量的内存分配情况。 五、编程题

1、某班有20个学生,每名学生的数据包括学号、姓名、3门课成绩,从键盘输入20名学生数据,要求打印出3门课的总平均成绩,以及最高分的学生数据(包括学号、姓名、3门课成绩、平均成绩)。

23


第9章 结构体和共用体12-07.doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:公共基础知识题库:社会公德与职业道德试题及答案解析(一)

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: