【C语言】11-结构体与共用体掌握
概述
1.数据的基本类型:整、实、字符。
2.数组是构造类型:每个元素为同一类型
3.有些问题仅用基本类型和数组来描述,无法反映其内在联系,如学生情况:
上述数据互相独立又相互关联,如:均与学号和姓名关联。需要将其组合成一个有机的整体,C语言可以将由不同类型数据组成的这种数据结构组织成一个组合项,称为结构体(structure)。
一、结构体
1.结构体是一种构造数据类型。
与其他高级语言中的“记录”类似。
2.定义:由相互关联的不同数据类型的数据组成的有机整体。
3.用途:为处理复杂的数据结构提供了手段。
为函数间传递不同类型的参数提供了便利。
4.关键字:struct
注意:这只是声明一种数据类型并没有定义变量。
二、结构体类型定义
注:结构体类型定义仅描述结构体的组成,不分配内存空间
定义结构体类型变量的方法
声明结构体类型时不分配存储单元,使用该类型定义变量时才分配存储单元。定义变量方法3种:
一、先声明结构体类型,再定义结构体变量
二、声明结构体类型的同时定义结构体变量
三、直接定义结构体类型变量
四、说明
1.结构体类型与结构体变量概念不同
⊙类型:不分配内存; 变量:分配内存
⊙类型:不能赋值、存取、运算; 变量:可以
2.结构体变量中的成员可单独使用,方法如普通变量;
3.结构体可嵌套
4.结构体成员名与程序中变量名可相同
5.结构体成员名与程序中变量名可相同,两者不代表同一个对象。
结构体变量的引用
一、引用规则
1. 结构体变量不能整体引用,只能引用变量成员
2.结构体成员本身又是一个结构体类型,则需要找到最低一级的成员。
3.结构体变量的成员与普通变量用法相同。
结构体变量的初始化
一、形式一
二、形式二
三、形式三
例:对结构体变量初始化
结构体数组
具有相同结构的结构体也可以组成数组
一、定义结构体数组:3种形式
二、结构体数组初始化
struct 结构名 结构数组名[数组长度]={初始数据};
三、结构体数组应用
例: 统计候选人选票
指向结构体类型数据的指针
一、指向结构体变量的指针
1.定义形式:
2.使用结构体指针变量引用成员形式
例: 指向结构体变量的指针的应用
3.注意区分以下三种运算
二、指向结构体数组的指针
1.结构体数组及其元素可用指针变量来指向
例: 指向结构体数组的指针的应用
三、用结构体变量和指向结构体的指针作函数参数
将一个结构体变量的值传递给另一函数 ,方法有3种:
1.用结构体变量的成员作函数实参----值传递
⊙注意形、实的类型要一致。
⊙传递的是结构体变量的地 址。
3.用结构体变量作参数----多值传递,效率低
⊙将结构体变量所占的内存单元的内容全部顺序传递给形参,要求形参与实参同类型。函数调用是单值传递,且形参占用内存单元,若形参的值被改变,不会返回主调函数。
例: 结构体变量stu有学号、姓名和3门课成绩,
在main函数中赋值,在print函数中打印输出。
例: 例11.5改为用结构体变量的指针作实参
用指针处理链表
一、链表概述
1.数组:静态分配存储单元,容易造成内存浪费。
2.链表:是重要的数据结构,它根据需要,动态分配内存单元 。
⊙数据部分:可有若干项(整、实、字符、结构体类型等)
4.链表各结点的特点:
⊙链表的每个结点实际上是一个结构体变量,它有若干成员组成,包括的内容有两部分:
--数据部分:整、实、字符、结构体等类型。
二、简单链表
例:建立简单链表,它由3个学生数据的结点组成。
输出各结点中的数据。
#include <stdio.h>
#define NULL 0
struct student
{long num; float score; struct student *next; };
void main( )
{struct student a,b,c,*head,*p;
a.num=99101;a.score=89.5; b.num=99102;b.score=90;
c.num=99103;c.score=85; head=&a;a.next=&b;b.next=&c;
c.next=NULL; p=head;
do { printf(“%ld %5.1f\n”,pnum,pscore);
p=pnext;
}while(p!=NULL);
}
P!=NULL条件不成立,循环结束。
本例所有结点是在程序中定义的,不是临时开辟的,用完也不能释放,这种链表称“静态链表”。
三、处理动态链表所需的函数
为处理动态链表,C提供了开辟和释放存储单元的函数:
1.malloc函数
⊙函数原型:void *malloc(unsigned int size);
2.calloc函数
⊙函数原型:void *calloc(unsigned n,unsigned size);
3.free函数
⊙函数原型:void free(void *p);
⊙作用:释放由p指向的内存区,使这部分内存区能被其它变量使用。P所指向的是最近一次calloc或malloc分配的存储区域。free函数无返回值。
4.注:旧版本提供的malloc和calloc函数得到的是指向字符型数据的指针。
四、建立动态链表
1.建立动态链表:是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系。
例:写一函数建立一个有3名学生数据的单向动态链表。
思路:
⑴ 设置3个指针变量head、p1、p2
head:指向链表头的指针变量,初始化head=NULL。
p2:指向结点成员next的指针变量。
⑵ 循环方式用malloc函数开辟第1个结点。n=1
输入数据,如果p1num !=0,
则head=p1结点链入链表,反之不链入。
⑶ 开辟第2个结点:n=2
输入数据。
如果p1num !=0,链入2结点,
方法:p2next=p1
⑷ 为建立第3个结点做准备:
p2=p1,腾出p1 。
⑸ 重复⑶⑷两步开辟第3个结点,并链入链表。
⑹ 再开辟新结点,由于num数据为0,退出循环。
并使p2next=NULL,虽然p1指向新结点但没有链入链表。
1.
#include “stdio.h”
#include “malloc.h”
#define NULL 0
#define LEN sizeof (struct student)
struct student
{long num; float score; struct student *next; };
int n;
struct student *creat(void)
{ struct student *head; struct student *p1, *p2;
n=0; p1=p2=(struct student *) malloc(LEN);
scanf(“%ld,%f”,&p1 num, &p1 score);
head=NULL;
while(p1 num !=0)
{n=n+1;
if(n==1)head=p1;
else p2 next=p1;
p2=p1; p1=(struct student *)malloc (LEN);
scanf(“%ld,%f”,&p1 num, & p1 score); }
p2 next=NULL; return(head);
}
2.
#include “stdio.h” /*改进后*/
#include “stdlib.h”
#define NULL 0
#define LEN sizeof (struct student)
struct student
{long num; float score; struct student *next; };
int n;
struct student *creat(void)
{ struct student *head; struct student *p1, *p2; flot s;
n=0; p1=p2=(struct student *) malloc(LEN);
scanf(“%ld,%f”,&p1 num,&s); p1 score=s;
head=NULL;
while(p1 num !=0)
{n=n+1;
if(n==1)head=p1;
else p2 next=p1;
p2=p1; p1=(struct student *)malloc (LEN);
scanf(“%ld ,%f”,&p1 num,&s); p1 score=s; }
p2 next=NULL; return(head);
}
五、输出链表
思路:
例:写一个输出链表的函数print。
六、对链表的删除操作
并不真从内存中抹掉,只是把它分离,再前后结点相链接。
例: 写一函数删除动态链表中指定的结点。
思路:本例以学号作为删除结点的标志(查找对象)。
⑴ 设两个指针变量p1和p2。从head开始,p1依次指向各结点查找num值是否等于要删除结点的学号。每次下移前使p2=p1。学号相等删除该结点,直至查到表尾。
⑵找到要删除的结点后:
①如果要删除的是第1结点,则head=p1next 。head指向第二结点,第一结点脱离。
②如果要删除的不是第1结点,则p2next=p1next 。P1指向的结点脱离。
③还要考虑链表为空和链表中没有要删除的结点的情况。
struct student *del(struct student *head,long num)
{ struct student *p1,*p2;
if(head = = NULL) {printf(“\n list null ! \n”); goto end; }
p1=head;
while(num !=p1 num && p1 next != NULL)
{ p2=p1; p1=p1 next; }
if(num == p1 num)
{ if(p1 = = head) head=p1 next;
else p2 next = p1 next;
printf(“delete: %d\n”,num);
n=n-1;
}
else printf(“%ld not been found ! \n”,num);
return(head);
}
七、对链表的插入操作
1.假设结点按成员的学号从小到大排列,按排序顺序插入结点。
2.将一个结点插入到链表中算法:
八、对链表的综合操作
1.用main函数作主调函数,调用前述建立、输出、删除、插入结点的函数。
共用体
一、共用体概念
1.构造数据类型,也叫联合体
2.用途:使几个不同类型的变量共占一段内存(相互覆盖)
3.类型定义形式:
4.共用体变量的定义
二、共用体变量的引用方式
1.3种方式等价:
2.引用规则
⊙不能引用共用体变量, 只能引用其成员
三、共同体类型数据的特点
⑴ 同一个内存段可以用来存放几种不同类型的成员,但在每 一瞬时只能存放其中一种,而不是同时存放几种。
⑵ 共用体变量中起作用的成员是最后一次存放的成员
⑷ 不能对共用体变量名赋值,也不能在定义共用体变量时初始化。但可以用一个共用体变量为另一个变量赋值
⑸ 不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可以使用指向共用体变量的指针(与结构体变量的这种用法相仿)。
⑹共用体类型可出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可出现在共用体类型定义中,数组也可作为共用体的成员。
例:设有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。现要求把它们放在同一表格中
void main( )
{ int n,i;
for(i=0;i<2;i++)
{ scanf(“%d,%s,%c,%c”,&person[i].num,&person[i].name,&person[i].sex,
&person.job);
if(person[i].job==‘s’) scanf(“%d”,&person[i].category.banji);
else if(person[i].job==‘t’) scanf(“%s”,&person[i].category.position);
else printf(“input error”);
}
printf(“\n”);
printf(“No. Name sex job class/position\n”);
for(i=0;i<2;i++)
{ if(person[i].job==‘s’)
printf(“%-6d %-10s %-3c %-3c %-6d\n”,person[i].num,person[i].name,
person[i].sex, person[i].job, person[i].category.banji);
else
printf(“%-6d %-10s %-3c %-3c %-6s\n”,person[i].num,person[i].name,
person[i].sex, person[i].job, person[i].category.position);
}
}
枚举类型
枚举类型是ANSI C新标准所增加的。
如果一个变量只有几种可能的值,可以定义为枚举类型。所谓“枚举是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。
一、枚举类型及其变量的定义形式:
enum 枚举类型名 {枚举元素列表} 枚举变量列表;
1.可以先声明类型再定义变量,或同时进行或直接定义变量。
其中sun, mon, … , sat等称为枚举元素或枚举常量。它们是用户定义的标识符。
2.说明:
⊙在编译中,对枚举元素按常量处理,它们不是变量,不能对它们赋值。
⊙枚举元素作为常量,它们是有值的,语言编译按定义时的顺序使它们的值为0,1,2,…。
⊙枚举值可以用来做判断比较。如:
--if(workday = = mon)…
--if(workday > sun)…
⊙一个整数不能直接赋给一个枚举变量。应先进行强制类型转换才能赋值。如:
--workday = (enum ewwkday)2;
例:口袋中有红、黄、蓝、白、黑5种颜色的球若干个。每次从口袋中先后取出3个球,问得到3种不同色的球的可能取法,打印出每种排列的情况。
要判断各球是否同色,应用枚举类型变量处理。设取出的球为i, j, k。根据题意,i、j、k分别是5种色球之一,并要求i≠j≠k。可以用穷举法,即一种可能一种可能地试,看哪一组符合条件。
用typedef定义类型
一、功能:用自定义名字为已有数据类型命名
二、类型定义简单形式:
例 typedef int INTEGER;
例 typedef float REAL;
三、typedef定义类型步骤
① 按定义变量方法写出定义体 如 int i;
② 将变量名换成新类型名 如 int INTEGER;
③ 最前面加typedef 如 typedef int INTEGER;
④ 用新类型名定义变量 如 INTEGER i,j;
四、类型定义可嵌套
引致:教材
C程序设计 (第二版)
谭浩强编著 清华大学出版社
内容:提供更好的学习资源