1.结构体类型
(1)结构体类型的说明
我们知道C语言的基本数据类型有整型、浮点型、字符型、双精度型。但是,在解决实际问题中,经常会遇到一组数据具有不同的数据类型。例如学生记录包含学号、性别、姓名、年龄、电话号码等。C语言中给出了另一种属于构造类型的结构体类型(或结构类型)。该类型是由若干“成员”组成,每一个成员可以是一个基本数据类型或者是一个结构类型。在说明和使用之前必须先定义它,也就是构造它。
结构类型的定义
结构类型包括两部分,一是结构类型名,二是结构成员。一般形式为:
成员列表由若干个成员组成,对每一个成员也必须做类型说明其形式为:
类型说明符 成员名;
成员名的命名应符合标识符的书写规定,例如构造学生结构体,它包括学号、姓名、性别、成绩等信息。
在这个结构定义中,结构名为student,第一个成员为num,整型变量;第二个成员为name,字符数组;第三个成员为sex,字符变量;第四个成员为score,实型变量。
注意:
① 在定义类型时,“struct”是定义结构类型的关键字因此不能被省略。
② 成员列表是对结构类型中各成员组成的一个说明,每个成员名后的分号不能省略。
③ 结构类型定义作为一条语句,其最后一个花括号外的分号不能省略。
(2)结构体类型的变量、数组和指针变量的定义
结构变量说明,包括下面三种方法。
① 先定义结构,然后说明结构变量。
例如:
说明了zhang和liu两个变量为student结构类型。
② 在定义结构类型的同时说明结构变量。
例如:
定义student结构类型的同时说明了zhang和liu两个变量为student结构类型。
③ 直接说明结构变量。
例如:
第三种方法与第二种方法的区别在于第三种方法中省略了结构名,而直接给出结构变量。
尽管这几种方法都可以使用,还是推荐使用第一种方式,先定义结构类型,再定义结构变量,程序的可读性更好。
既然自定义的结构类型和基本数据类型是一样的。因此也可以定义结构类型的指针、数组。
例如:
struct student class[3];
struct student *pstu1;
说明数组class为student结构类型,指针变量pstu1为student结构类型。
另外,在C语言程序设计中有时要用到结构类型的嵌套。
嵌套结构类型的定义有以下两种形式。
形式一:
例如:
形式二:
例如:
建议采用第二种形式。
(3)给结构体变量、数组赋初值
① 给结构体变量赋值就是给各个成员赋值。
因为结构成员按定义中的先后顺序排列的,所以在对结构变量初始化时,同数组的初始化类似。
简单结构体变量的初始化,在定义变量的同时初始化该变量。例如:
另外,给结构体变量赋值也可以先定义结构变量,然后分别对成员进行初始化赋值也是合法的,但是需要用到成员访问运算符“.”。
例如:
zhaox.num=158;
② 结构体数组的初始化
对结构体数组的初始化赋值,和结构变量的初始化是一样的,如果对全部元素做初始化赋值,也可以不给出数组长度。
例如:计算学生的平均成绩。
本程序中,定义了一个外部结构名为student的结构体数组stu,共有3个元素,并做了初始化赋值。在main函数中用循环语句逐个累加各个元素的score值并保存在变量sum中,循环结束后计算平均分,并输出平均成绩。
(4)结构体变量中的数据引用
在程序中,对结构变量的赋值、输入、输出、运算等都是通过结构变量的成员来实现的。它们的使用和普通变量一样。
表示结构变量成员的一般形式如下:
结构变量名.成员名(www.xing528.com)
sun.num //结构名为teacher的结构变量sun的编号
stu[i].score //结构名为student的结构体数组stu第i个元素的成绩
另外,嵌套结构则必须逐级找到最低级的成员才能使用。
例如:
shen.birthday,year //结构名为teacher的结构变量shen的出生年份
下面程序的功能是建立职工的记录,可以使读者加深理解结构体变量的数据引用。
在本例中定义了一个结构名为worker的结构体数组people,在第一个循环语句中,分别输入各个元素中成员的值,在第二个循环语句中输出各个元素中姓名成员和工资成员的值。
(5)利用结构体构成链表
每次为一个结构动态分配的内存空间称之为一个结点。使用动态分配内存空间时,相邻两个结点之间可以是不连续的。此时,结点之间的联系一般用指针来实现,即在结点结构中定义一个成员项用来存放其下一个结点的首地址,这个用于存放地址的成员称之为指针域。
例如:
这里的next是成员名,它是指针类型的,指向struct student类型数据。
当第一个结点的指针域内存入第二个结点的首地址,在第二个结点的指针域内又存放第三个结点的首地址,如此串连下去直到最后一个结点。最后一个结点因无后续结点连接,其指针域可赋值为0。这样一种连接方式,在数据结构中成为链表。
例如:用链表结构组织学生数据的存储如图7-1所示。
图7-1 链表结构示意图
在此链表的示意图中,每个结点都分为两个域,一个是数据域,存放各种实际的数据,如学号num,姓名name,性别sex和成绩score等。另一个是指针域,存放下一个结点的首地址。其中第0个结点称为头结点,它存放有第一个结点的首地址,它没有数据,只是一个指针变量。最后一个结点因没有后续结点,它的数据域存放最后一个学生的实际数据,而指针域赋值0。
对链表的主要操作有建立链表、结构的查找与输出、结点数据的删除和结点数据的插入等。
① 动态链表的建立
动态链表的创建有两种方式。
● 先进先出单链表。在建立单链表时,将每次生成的新结点总是插入到当前链表的表尾作为尾结点。
● 后进先出单链表。在建立单链表时,将每次生成的新结点总是插入到当前链表的表头结点之后作为当前链表的首结点。
这两种方法大同小异,主要区别在于对插入结点的前一结点跟踪及对应指针域的处理。在建立链表时,一般选择先进先出单链表的创建方式。
具体创建步骤:
第一步,创建空表,定义头结点指针域为空。
第二步,准备建表,定义两个指针p和q,约定p恒指向链表末尾结点,q恒指向待插入结点。
第三步,申请新结点,作为待插入结点。
第四步,插入新结点,将新结点插入到链表的末尾,作为当前末尾结点。
第五步,只要待插入结点个数小于链表中预定结点个数,则转到第三步继续插入。
② 结构的查找与输出
利用一个工作指针p,从头到尾依次指向链表中的每一个结点;当指针指向某一个结点时,就输出该结点数据域中的内容,直到遇到链表的结束标志为止。如果是空链表,就只输出有关信息并返回调用函数。
链表的输出过程有以下几步:
第一步,确定表头,并约定指针p恒指向待输出结点。
第二步,若待输出结点非空,则输出结点数据域的值;若为空,则退出。
第三步,跟踪链表地址域,找到下一个待输出结点。
第四步,转到第二步,继续输出。
例如:
下面的程序段是利用前面的student结构类型构成的链表,输出每个学生的姓名。
③ 结点数据的删除
为了删除单向链表中的某个结点,首先要找到待删除结点的前趋结点,然后将此前趋结点的指针域去指向待删除结点的后续结点(p->next=q->next),最后释放被删除结点所占存储空间(free(q))即可。
例如:
单向链表中的结点结构为:
则将q所指结点从链表中删除,同时要保持链表的连续,即q->next中存放的是r所指结点的首地址,如果将r所指结点的首地址存于p->next中,就可以删除q所指结点,并保持链表的连续(即p所指结点与r所指结点相连)。
p->next=q->next;
或p->next=r;
或p->next= p->next->next;
在有n个结点的单链表中,删除第i个结点的操作如图7-2所示。
图7-2 删除单链表的第i个结点示意图
④ 结点数据的插入
在单向链表中插入结点,首先要确定插入的位置。当待插结点插在指针p所指结点之前称为前插;待插结点插在指针p所指结点之后称为后插。
2.已知类型的别名
C语言允许用typedef定义已知类型的别名。这样可以简化程序的输入,也更易于程序的理解和移植。
用typedef定义已知类型的别名格式为:
typedef 类型名 标识符;
其中的“类型名”必须是在此之前已经定义的类型标识符;“标识符”是用户定义的用作已知类型别名的标识符。
typedef语句的作用仅仅是用“标识符”来代表已经存在的“类型名”,并未产生新的数据类型,原有类型名仍然有效。
例如:
typedef int zhengx; //int的别名为zhengx
zhengx a,b; //说明a和b为整型变量
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。