1
第 11章 结构体和共用体主讲 福州大学数学与计算机学院 韩晓芸
E-mail,hxy@fjtv.net
第 11章 结构体与共用体
2
第一节 结构体概述第二节 结构体类型和结构体变量的定义第三节 结构体类型变量的引用第四节 结构体与数组第五节 结构体与指针第六节 结构体的应用 --链表第七节 共用体 和枚举类型第 八节 使用 typedef定义数据类型第 11章 结构体与共用体
3
简单变量 只能保存 一个 所定义类型的数据;
数组 则可以 定义保存 一组相同类型 的数据,保存在连续存储空间中。
为满足将一些 不同数据类型、但又相互关联的一组数据,组合成一个有机整体使用,C
语言提供一种称为,结构体,的数据类型。
构造 (复合 )类型,
结构体 中允许用户定义不同类型的数据,包含简单变量、数组、结构体、指针等 。
第一节 结构体概述第 11章 结构体与共用体
4
第一节 结构体概述
结构体构造一个 结构体类的数据类型 的一般形式:
struct 结构类型名
{类型标识符 成员名 ;
类型标识符 成员名 ;
:
类型标识符 成员名 ;
};
struct student
{long num;
char name[20];
char sex;
char addr[30];
};
第 11章 结构体与共用体
5
第二节 结构体类型和结构体变量的定义
定义结构体类数据类型变量的 三种形式
结构体类数据类型变量的 初始化第 11章 结构体与共用体
6
形式一
先构造结构体类的数据类型,后定义具有这种构造的变量。 例如,
struct student
{long num;
char name[20];
char sex;
char addr[30]; };
struct student stud1,stud2;
struct 结构体类型名
{类型标识符 成员名 ;
:
类型标识符 成员名 ;
};
struct 结构体类型名变量名 1,变量名 2...;
第 11章 结构体与共用体
7
在构造结构体类的数据类型时同时定义具有这种结构的变量。 例如,
形式二
struct student
{long num;
char name[20];
char sex;
char addr[30];
}stud1,stud2;
struct 结构体类型名
{类型标识符 成员名 ;
:
类型标识符 成员名 ;
}变量名 1,变量名 2,...;
第 11章 结构体与共用体
8
利用无名结构体类型定义变量。 例如:
形式三
struct
{long num;
char name[20];
char sex;
char addr[30];
}stud1,stud2;
struct
{类型标识符 成员名 ;
:
类型标识符 成员名 ;
}变量名 1,变量名 2,......;
第 11章 结构体与共用体
9
变量的初始化
struct student
{long num;
char name[20];
char sex;
char addr[30];
}stud1={9708,"Liwei",'F',"144BeijingRoad"};
第 11章 结构体与共用体
10
说明
结构体的类型不分配存储单元,用结构体类数据类型定义的变量会分配存储空间;
对 结构体中的成员,可以单独使用,它的作用与地位相当于普通变量;
一个结构体的成员名字不能相同,但两个结构体中可以使用同名成员,成员名也可以与程序中的变量名相同,二者代表不同的对象 ;
成员也可以是一个结构体变量 (嵌套)。
第 11章 结构体与共用体
11
struct date
{int month;
int day;
int year;
};
struct stud
{long num;
char name[20];
struct date birthday;
char sex;
float score[10];
}st1;
成员也可以是一个结构体变量 (嵌套)。例如:
第 11章 结构体与共用体
12
第三节 结构体类型变量的引用
1,对结构体变量的使用是通过对数据类型为基本类型的成员的引用来实现的。引用结构体成员的一般形式如下:
结构变量名,成员名例如,st1.sex=?F?;
其中的圆点符号称为成员运算符,它的运算级别最高。
第 11章 结构体与共用体
13
第三节 结构体类型变量的引用
2,引用结构体类数据类型变量的成员的方式是由
“整体到局部” 来指明的。
– 若指明的结构体成员又是一个结构体类数据类型,则 可以使用多个成员运算符,例如:
st1.birthday.month=9;
– 若指明的成员是一个数组,则不能整体引用,
但可以使用该成员的某个元素,如:
st1.score[0]=65;
第 11章 结构体与共用体
14
3,用,&” 运算符可以取结构体变量的首地址和某个成员的首地址。 例如:
printf(“%d\n”,&st1);
scanf(“%c\n”,&st1.sex);
scanf(“%d\n”,&st1.birthday.month);
对数组成员的首地址,可以省略地址运算符,如:
scanf(“%s”,st1.name);
4,对成员变量可以象普通变量一样进行各种运算,
例如:
sum=st1.score[0]+ st1.score[1]+ st1.score[2]
第 11章 结构体与共用体
15
结构体变量的输入和输出
C语言不允许把一个结构体变量作为一个整体进行输入或输出的操作。 例如:
struct
{long num;
char name[20];
}stud;
不允许
scanf("%d",&stud); printf("%d",stud);
可以用
scanf("%ld,%s",&stud.num,stud.name);
printf("%ld,%s",stud.num,stud.name);
第 11章 结构体与共用体
16
注意:
1、由于结构体是由不同类型成员组成的,所以 在用 scanf函数输入不同类型数据时有时会出现预料不到的事情 。 例如:
main()
{struct
{int i;
char ch1;
char ch2;
}tt;
scanf(“%d%c%c”,&tt.i,&tt.ch1,&tt.ch2);
printf(“i=%d,ch1=%c,ch2=%c”,tt.i,tt.ch1,tt.ch2);}
输入 125 a b
运行结果?
i=125,ch1=,ch2=a
第 11章 结构体与共用体
17
2,尽量避免用一个 scanf函数输入包含字符数据在内的一组不同类型的数据,以免出错。 处理办法:
各种数据都用 gets函数输入,然后再用转换函数进行转换:
– atoi() 将字符串转换成整型
– atof() 将字符串转换成 double型实数
– atol() 将字符串转换成长整型
这三个函数要用 #include命令将,stdlib.h”文件包含进来 。
第 11章 结构体与共用体
18
输入示例:
#include "stdlib.h"
#include "stdio.h"
main()
{int i; char ch,ch1,ch2; char numstr[10];
gets(numstr);
i=atoi(numstr);
ch1=getchar();
ch=getchar();
ch2=getchar();
printf("i=%d,ch1=%c,ch2=%c\n",i,ch1,ch2);}
程序运行结果如下:
128
a
b
i=128,ch1=a,ch2=b
第 11章 结构体与共用体
19
第四节 结构体数组
结构体数组的定义
结构体数组的初始化
结构体数组的引用第 11章 结构体与共用体
20
先定义结构体类型,再定义结构体数组。
struct student
{long num;
char name[20];
int age;
float score; };
struct student stud[3];
定义,方法一第 11章 结构体与共用体
23
结构体数组在内存中的存储,
( 1)按数组下标由小到大
( 2)在给每个数组元素的空间内再按照成员的顺序连续存放。
stud[0] 9701,"liMing",20,98
stud[1] 9702,"WangDan",20,95
stud[2] 9703,"LiHui",19,80
9701
LiMing
20
98
9702
WangDan
20
95
9703
LiHui
19
80
第 11章 结构体与共用体
24
初始化
struct student
{long num;
char name[20];
int age;
float score;
}stud[3]={{9701,"liMing",20,98},
{9702,"WangDan",20,95},
{9703,"LiHui",19,80}};
第 11章 结构体与共用体
25
引用
引用某结构体数组元素的一个成员,可用以下形式:
stud[i].num
成员名序号数组名第 11章 结构体与共用体
26
说明
不能将结构体数组元素作为一个整体直接进行输入或输出,只能以单个成员为对象进行输入输出。
例如:
scanf("%s",stud[1].name);
printf("%ld,%s,%d,%f",stud[1].num
stud[1].name,stud[1].age,stud[1].score);
第 11章 结构体与共用体
27
输入示例,输入 3个学生的信息并输出。
#include "stdlib.h"
#include "stdio.h"
struct stud
{long num;
char name[20];
char sex;
int age;
float score;};
第 11章 结构体与共用体
28
main()
{struct stud student[3];
int i; char ch; char numstr[20];
for(i=0;i<3;i++)
{gets(numstr); student[i].num=atol(numstr);
gets(student[i].name);
student[i].sex=getchar(); ch=getchar();
gets(numstr); student[i].age=atoi(numstr);
gets(numstr); student[i].score=atof(numstr);}
for(i=0;i<3;i++)
printf("%ld %-15s %3c %6d
%6.2f\n",student[i].num,student[i].name,
student[i].sex,student[i].age,student[i].score);}
第 11章 结构体与共用体
29
例 1,在 N名毕业生中查找上海籍的学生。
#define N 3
struct
{char name[20];
char sex[3];
int age;
int score;
char addr[30];
}stud[N]={{"张利平 ","男 ",23,79,"上海 "},
{"钱龙 ","女 ",24,85,"南京 "},
{"刘其山 ","男 ",22,66,"上海 "}};
第 11章 结构体与共用体
30
例 1,在 N名毕业生中查找上海籍的学生。
main( )
{int i;
for(i=0;i<N;i++)
if(strcmp(stud[i].addr,"上海 ")==0)
printf("%s %s %d %d %s\n",
stud[i].name,stud[i].sex,stud[i].age,
stud[i].score,stud[i].addr);}
第 11章 结构体与共用体
31
指向结构体变量的指针变量
指向结构体数组的指针变量
处理结构体类数据的函数参数第五节 结构体与指针第 11章 结构体与共用体
32
指向结构体变量的指针
1,结构体变量的指针:
结构体变量的起始地址 。
2,定义形式:
struct 结构类型名 *变量名 ;
例如:
struct student *p,stud;
p=&stud;
第 11章 结构体与共用体
33
3、用此指针变量可间接访问它所指向的结构体类数据类型变量的各个成员。
指针变量名 ->成员名 例如,p->age=18;
(*指针变量名 ).成员名 例如,(*p).age=18;
第 11章 结构体与共用体
34
说明:
– *p两册的括号不能省略,因为成员运算符,,”
优于,*”运算符,*p.age等价于 *(p.age)
– p已定义为指向一个结构体类型的指针变量,
它只能指向结构体变量而不能指向它其中的一个成员。 如,p=&stud1.age是错误的 。
–,->” 运算符优先级别最高,例如:
p->age+1 等价于 (p->age)+1
++p->age 等价于 ++(p->age)
第 11章 结构体与共用体
35
例 2:在三个学生中找出成绩最好的学生并显示该学生的情况。
struct student
{char name[20];
char sex[3];
int age;
int score;
char addr[30]; };
struct student stud1={"张利平 ","男 ",23,79,"上海 "};
struct student stud2={"钱 龙 ","女 ",24,85,"南京 };
struct student stud3={"刘其山 ","男 ",22,66,"上海 "};
第 11章 结构体与共用体
36
main()
{int i;
struct student *p=&stud1;
if(p->score<stud2.score)
p=&stud2;
if(p->score<stud3.score)
p=&stud3;
printf("%s%s%d%d%s\n",p->name,
p->sex,p->age,p->score,p->addr);}
第 11章 结构体与共用体
37
指向结构体数组的指针
一个指针变量指向一个结构体数组,也就是将该数组的起始地址赋给此指针变量。
– 定义一个结构体类型
– 再定义属于这种结构体类型的数组和指向这种结构体类型的指针变量
– 把数组的首地址赋给指针变量第 11章 结构体与共用体
38
例 3,用指向结构体数组的指针变量改写例 1
#define N 3
struct
{char name[20];
char sex[3];
int age;
int score;
char addr[30];
}stud[N]={{"张利平 ","男 ",23,79,"上海 "},
{"钱 龙 ","女 ",24,85,"南京 "},
{"刘其山 ","男 ",22,66,"上海 "}},*p=stud;
第 11章 结构体与共用体
39
例 3:用指向结构体数组的指针变量改写例 1
main()
{int i;
for(i=0;i<N;i++,p++)
if(strcmp((*p).addr,"上海 ")==0)
printf("%s %s %d %d %s\n",
(*p).name,
(*p).sex,(*p).age,(*p).score,(*p).addr);
}
第 11章 结构体与共用体
40
例 4:改写例 1,每查找到一个上海籍学生就调用函数 print()
把该学生的情况显示在屏幕上。
#define N 3
#include "string.h "
struct
{char name[20];
char sex[3];
int age;
int score;
char addr[30];
}stud[N]={{"张利平 ","男 ",23,79,"上海 "},
{"钱 龙 ","女 ",24,85,"南京 "},
{"刘其山 ","男 ",22,66,"上海 "}},*p=stud;
处理结构体类数据的函数参数第 11章 结构体与共用体
41
处理结构体类数据的函数参数
main()
{int i;
for(i=0;i<N;i++)
if(strcmp(stud[i].addr,"上海 ")==0)
print(stud[i].name,stud[i].sex,stud[i].age,
stud[i].score,stud[i].addr); }
void print(char name[],char sex[],int age,int
score,char addr[])
{printf("%s %s %d %d
%s\n",name,sex,age,score,addr);}
第 11章 结构体与共用体
44
例 7,改写例 4中函数 print和 main,用指向结构体类数据类型的指针变量作为函数的形参和实参。
#include "string.h "
main()
{int i;
for(i=0;i<N;i++,p++)
if(strcmp((*p).addr,"上海 ")==0)
print(p);}
print(struct student *p)
{printf("%s %s %d %d %s\n",p->name,
p->sex,p->age,p->score,p->addr);}
第 11章 结构体与共用体
45
链表概述
指针基本操作
建立链表
输出链表第六节 结构体的应用 --链表
插入结点
删除结点
结点排序第 11章 结构体与共用体
46
链表概述单链表
head
1382 9701 65 9702 3282 9703 null
1382 1296 3282
1296 76 70
循环单链表
head
1382 9701 65 9702 3282 9703 1382
1382 1296 3282
1296 76 70
双向链表
1506
1382 1506 65 1382 3282 1296 null
1382 1296 3282
1296 76 70
第 11章 结构体与共用体
47
2,单链表用包含指针项的结构体变量构成结点两部分组成:
– 对用户有用的数据
– 用来存放下一个结点地址的一个指针类型数据项
struct stud_ score
{ long num;
float score;
struct stud_score *next;
};
num和 score成员用来存放学号和成绩,next是指针变量,
它指向 struct stud_score类型的变量。 next指针项必须指向与 next所在的结点是同一类型的结点 。
第 11章 结构体与共用体
48
3,用于动态存储分配的函数
(1)malloc函数其作用是在内存开辟指定大小的存储空间,并将此存储空间的起始地址作为函数值带回。
malloc函数的形式:
void *malloc( unsigned int size)
或 char *malloc( unsigned int size)
它的形参 size为无符号整型。函数值为指针(地址),
例如:用 malloc(8)来开辟一个长度为 8个字节的内存空间,如果系统分配的此段空间的起始地址为 1362,则 malloc
(8)的函数返回值为 1362。
如果内存缺乏足够大的空间进行分配,则 malloc函数值为“空指针”,即地址为 0。
第 11章 结构体与共用体
49
(2)calloc函数其函数模型为:
void *calloc(unsigned int n,unsigned int size)
它有两个形参 n和 size,其作用是 分配 n个大小为
size字节的空间,例如:用 calloc(10,16)可以开辟 10个(每个大小为 16字节)的空间,共 160字节。函数返回值为该空间的首地址。
第 11章 结构体与共用体
50
(3) free函数
其函数模型为:
void free( void *ptr)
其作用是 将指针变量 ptr指向的存储空间释放,
系统可以另分配。注意,ptr值不能是任意的地址,而是在程序中执行过的 malloc或 calloc函数所返回的地址。 free函数无返回值 。
第 11章 结构体与共用体
51
(4) realloc函数
用来使已分配的空间改变大小,即重新分配 。
其函数模型为:
void *realloc( void *ptr,unsigned int size)
其作用是 将 ptr指向的存储区(用 malloc函数分配的)的大小改为 size个字节 。 它的返回值是新的存储区的首地址。
使用这些函数时要用 #include命令将 stdlib.h文件包含进来。但有的系统用 malloc.h而不是 stdlib.h。
在使用时请注意系统的规定。 有的系统则不要求包括任何“头文件”。
第 11章 结构体与共用体
52
指针基本操作
有定义
#define LEN sizeof(struct node)
struct node
{ int data;
struct node *next;
};
struct node *p,*q;
第 11章 结构体与共用体
53
操作 (1)
p=(struct node *)malloc(LEN)
功能申请一个结点的空间,将地址送入 p
操作后状态
p
第 11章 结构体与共用体
54
操作 (2)
free(p)
功能释放指针变量 p
p
操作后状态
p
null
操作前状态第 11章 结构体与共用体
55
操作 (3)
p=q
功能指针变量 p指向 q所指的结点
p
操作前状态操作后状态
q p
第 11章 结构体与共用体
56
操作 (4)
p=q->next
功能指针变量 p指向 q所指的结点的后一个结点
操作前状态操作后状态
q p
第 11章 结构体与共用体
57
操作 (5)
p=p->next
功能指针变量 p向后移动一个结点
操作前状态操作后状态
p p
第 11章 结构体与共用体
58
操作 (6)
p->next=q
功能将指针 q所指结点接到 p所指结点之后
操作前状态?操作后状态
p
q
第 11章 结构体与共用体
59
操作 (7)
p->next=null
功能将指针 p所指结点与后续结点截断
操作前状态?操作后状态
p
第 11章 结构体与共用体
60
建立链表 (方法一 )
链表只能在程序执行过程中 动态 地生成。 有两种方法:
– 方法一基本思想假设前 i-1个元素已经存放到以 head为头指针的单链表中,且指针 p指向该链表中最后一个结点,
则读入第 i个元素后,首先动态生成一个结点,
其数据域存放元素,然后插入到 p结点之后,并令 p指向新插入的结点。若 i为 1,则令头指针
head指向新插入的结点。
第 11章 结构体与共用体
61
定义,#define LEN sizeof(struct student)
struct student
{long num;
int score;
struct student *next; };
p1=(struct student *)malloc(LEN);
scanf("%ld%d",&p1->num,&p1->score);
while(p1->num!=0)
{if(++n==1) head=p1;
else p2->next=p1;
p2=p1;
p1=(struct student *)malloc(LEN);
scanf("%ld%d",&p1->num,&p1->score);}
第 11章 结构体与共用体
62
head
p2 9701 87 9702
p1
67
(n=2)
(a)
head
p2 9701 87 9702
p1
67 (b)
head
p2
9701 87 9702 67
p1
(c)
head 9701
p1
87
(n=1)p2
第 11章 结构体与共用体
63
方法一步骤
– 定义结构体及指向这种结构体的三个指针变量
head,p1,p2
– head=null,n=0
– 申请一个结点,让 p1指向此结点
– 对 p1指向的结点的各个成员赋值
– 当 p1->num不是零
n=n+1
if(n==1)head=p1; else p2->next=p1
p2=p1
开辟一个新结点使 p1指向它
读入一个数据给 p1所指的结点
– 表尾结点的指针变量置 null
第 11章 结构体与共用体
64
函数实现
#include <stdio.h>
#include <alloc.h>
#include <stdlib.h>
#define NULL 0
#define LEN sizeof(struct student)
struct student
{ long num;
int score;
struct student *next;
};
int n;
第 11章 结构体与共用体
65
struct student *creat()
{struct student *p1,*p2,*head;
head=NULL;n=0;
p1=(struct student *)malloc(LEN);
scanf("%ld%d",&p1->num,&p1->score);
while(p1->num!=0)
{if(++n==1) head=p1;
else p2->next=p1;
p2=p1;
p1=(struct student *)malloc(LEN);
scanf("%ld%d",&p1->num,&p1->score);}
p2->next=NULL;
free(p1);
return(head);}
第 11章 结构体与共用体
71
基本思想:
– 从 p指向的第一个结点开始,检查该结点中的
num值是否等于要删除的哪个学号。 如果相等就删除该结点,如不相等就将 p后移一个结点,再如此进行下去。
删除结点第 11章 结构体与共用体
72
head a1 ai ai+1ai-1
p2 p1
an null
a1 ai ai+1a2
p2 p1
an nullhead
p1
a1 ai ai+1a2
p2
an null
head
head=p1->next;
head a1 aiai-1
p1
p2
ai+1 an null
p2->next=p1->next;
第 11章 结构体与共用体
73
– 删除结点步骤
定义结构体及指向这种结构体的二个指针变量 p1,p2
p1=head
当 p1->num!=num并且 p1不为空 /*查找要删除的结点 */
– p2=p1
– p1=p1->next
if (p1->num等于 num) /*找到要删除结点 */
– if(p1是第一个结点 ) head=p1->next
– else p2->next=p1->next
– free(p1)
– n=n-1
else 打印没有找到要删除的结点第 11章 结构体与共用体
74
struct student *del(struct student *head,long num)
{struct student *p1,*p2;
p1=head;
while(p1->num!=num&&p1!=NULL)
{p2=p1; p1=p1->next; }
if(p1==NULL)
printf("\nthere is not%ld\n",num);
else {if(p1==head) head=p1->next;
else p2->next=p1->next;
free(p1); printf("\ndelete:%ld\n",num);
n=n-1;}
return(head);}
第 11章 结构体与共用体
105
枚举类型的定义
enum 枚举类型名 {取值表 };
如,
enum week{Sun,Mon,Tue,Wed,Thu,Fri,Sat};
枚举变量的定义 ─与结构体变量类似
( 1) 间接定义先定义枚举类型,再定义枚举型变量 。
例,enum week workday;
枚举类型和变量的定义第 11章 结构体与共用体
106
枚举变量的定义 ─与结构体变量类似
( 2) 直接定义即在定义枚举类型的同时定义枚举型变量 。
例,enum week
{Sun,Mon,Tue,Wed,Thu,Fri,Sat}
workday;
说明
( 1) 枚举型仅适应于取值 个数有限 的数据 。
( 2) 取值表中的值称为枚举元素,其含义由程序解释 。
枚举类型和变量的定义第 11章 结构体与共用体
107
说明
(3)枚举类型的变量的取值只能在列表之中 。 另外,
枚举元素还有一个顺序号值 (从0开始 ),枚举元素比较的规则是,序号大者为大 !
如上例中的 Sun=0,Mon=1,……,Sat=6,
所以 Mon>Sun,Sat最大 。
(4)枚举元素的值也是可以人为改变的:在定义时由程序指定 。
如:设 enum weekdays {Sun=7,Mon=1,Tue,
Wed,Thu,Fri,Sat};则 Sun=7,Mon=1,
从 Tue=2开始,依次增 1。
枚举类型和变量的定义第 11章 结构体与共用体
108
说明
( 5) 枚举值可以用来做条件判断,如,
if (workday==mon) …
if (workday>tue) …
( 6)一个整数不能直接赋值给一个枚举变量,如,
workday=2;(?)
workday=(enum weekday) 2; (?)
workday=tue; (?)
枚举类型和变量的定义第 11章 结构体与共用体
109
练习题将每年的 12个月定义成枚举型,定义一个变量,用户输入一个月份的数字,输出相应的英文字符串 。
enum mon{jan,… } month;
switch(i)
{
case 0,printf(“JAN”);
…,
}
枚举类型和变量的定义