第十一章 结构体与共用体
§ 11.1 结构体
结构体是 一种 构造 数据类型
用途:把 不同类型 的数据组合成一个整体 -------
自定义 数据类型
结构体类型定义
struct [结构体名 ]
{
类型标识符 成员名;
类型标识符 成员名;
…………….
};
成员类型可以是基本型或构造型struct是 关键字,
不能省略合法标识符可省,无名结构体例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
name
num
sex
age
score
addr
2字节
2字节
20字节
1字节
4字节
30字节

…..
结构体类型定义描述结构的组织形式,不分配内存结构体类型定义的 作用域例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
struct student stu1,stu2;
§ 11.2 结构体变量的定义
先定义结构体类型,再定义结构体变量
一般形式,struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
…………….
};
struct 结构体名 变量名表列 ;
例 #define STUDENT struct student
STUDENT
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
STUDENT stu1,stu2;
定义结构体类型的同时定义结构体变量一般形式,struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
…………….
}变量名表列 ;
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
直接定义结构体变量一般形式,struct
{
类型标识符 成员名;
类型标识符 成员名;
…………….
}变量名表列 ;
例 struct
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
用 无名结构体 直接定义变量 只能一次
说明
结构体类型与结构体变量概念不同
类型,不分配内存 ; 变量,分配内存
类型,不能赋值、存取、运算 ; 变量,可以
结构体可嵌套
结构体成员名与程序中变量名可相同,不会混淆
结构体类型及变量的作用域与生存期例 struct date
{ int month;
int day;
int year;
};
struct student
{ int num;
char name[20];
struct date birthday;
}stu;
num name birthdaymonth day year
例 struct student
{ int num;
char name[20];
struct date
{ int month;
int day;
int year;
}birthday;
}stu;
num name birthdaymonth day year
§ 11.3 结构体变量的引用
引用规则
结构体变量 不能整体引用,只能引用变量 成员
可以将一个结 构体变量赋值给另一个结构体变量
结构体嵌套时 逐级引用成员 (分量 )运算符优先级,1
结合性,从左向右引用方式,结构体变量名,成员名例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
stu1.num=10;
stu1.score=85.5;
stu1.score+=stu2.score;
stu1.age++;
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
printf(“%d,%s,%c,%d,%f,%s\n”,stu1); (?)
stu1={101,“Wan Lin”,?M?,19,87.5,“DaLian”}; (?)
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
stu2=stu1; ( )
例 struct student
{ int num;
char name[20];
struct date
{ int month;
int day;
int year;
}birthday;
}stu1,stu2;
num name birthdaymonth day year
stu1.birthday.month=12;
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
if(stu1==stu2)
…….,(?)
§ 11.4 结构体变量的初始化
形式一,struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
…………….
};
struct 结构体名 结构体变量 ={初始数据 };
例 struct student
{ int num;
char name[20];
char sex;
int age;
char addr[30];
};
struct student stu1={112,“Wang Lin”,?M?,19,“200 Beijing Road”};
形式二:
struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
…………….
}结构体变量 ={初始数据 };
例 struct student
{ int num;
char name[20];
char sex;
int age;
char addr[30];
}stu1={112,“Wang Lin”,?M?,19,“200 Beijing Road”};
形式三:
struct
{
类型标识符 成员名;
类型标识符 成员名;
…………….
}结构体变量 ={初始数据 };
例 struct
{ int num;
char name[20];
char sex;
int age;
char addr[30];
}stu1={112,“Wang Lin”,?M?,19,“200 Beijing Road”};
§ 11.5 结构体数组
结构体数组的定义三种形式:
形式一,
struct student
{ int num;
char name[20];
char sex;
int age;
};
struct student stu[2];
形式二
struct student
}stu[2];
形式三 num
name
sex
age
num
name
sex
age
stu[0]
stu[1]
25B
结构体数组初始化例 struct
{ int num;
char name[20];
char sex;
int age;
}stu[ ]={{……},{……},{……}};
顺序初始化,
struct student
{ int num;
char name[20];
char sex;
int age;
};
struct student stu[ ]={100,“Wang Lin”,?M?,20,
101,“Li Gang”,?M?,19,
110,“Liu Yan”,?F?,19};
例 struct student
{ int num;
name[20];
char s x;
int age;
}stu[ ]={{……},{……},{……}};
分行初始化,
struct student
{ int nu ;
char na e[20];
char sex;
int age;
};
struct student stu[ ]={{100,,ang Lin”,,20},
{101,“Li Gang”,,19},
{110,“Liu Yan”,?F?,19}};
全部初始化时维数可省
结构体数组引用引用方式,结构体数组名 [下标 ].成员名
stru t student
{ int num;
char name[20];
char sex;
int age;
}str[3];
stu[1].age++;
strcpy(stu[0].name,”ZhaoDa”);
例 统计后选人选票
struct person
{ char name[20];
int count;
}leader[3]={“Li”,0,“Zhang”,0,”Wang“,0};
main()
{ int i,j; char leader_name[20];
for(i=1;i<=10;i++)
{ scanf("%s",leader_name);
for(j=0;j<3;j++)
if(strcmp(leader_name,leader[j].name)==0)
leader[j].count++;
}
for(i=0;i<3;i++)
printf("%5s:%d\n",leader[i].name,leader[i].count);
}
name count
Li
Zhang
Wang
0
0
0
§ 11.6 结构体和指针
指向结构体变量的指针
定义形式,struct 结构体名 *结构体指针名 ;
例 struct student *p;
使用结构体指针变量引用成员形式 存放结构体变量在内存的起始地址
num
name
sex
age
stu
pstruct student{ int num;
char name[20];
char sex;
int age;
}stu;
struct student *p=&stu;
(*结构体指针名 ).成员名 结构体指针名 ->成员名 结构体变量名,成员名指向运算符优先级,1
结合方向:从左向右例 指向结构体的指针变量
main()
{ struct student
{ long int num;
char name[20];
char sex;
float score;
}stu_1,*p;
p=&stu_1;
stu_1.num=89101;
strcpy(stu_1.name,"Li Lin");
p->sex='M';
p->score=89.5;
printf("\nNo:%ld\nname:%s\nsex:%c\nscore:%f\n",
(*p).num,p->name,stu_1.sex,p->score);
}
例 int n;
int *p=&n;
*p=10;? n=10
struct student stu1;
struct student *p=&stu1;
stu1.num=101;? (*p).num=101
指向结构体数组的指针例 指向结构体数组的指针
struct student
{ int num;
char name[20];
char sex;
int age;
}stu[3]={{10101,"Li Lin",'M',18},
{10102,"Zhang Fun",'M',19},
{10104,"Wang Min",'F',20}};
main()
{ struct student *p;
for(p=stu;p<stu+3;p++)
printf("%d%s%c%d\n",p->num,p->name,p->sex,p->age);
}
num
name
sex
age
stu[0]
p
stu[1]
stu[2]
p+1
用指向结构体的指针作函数参数
用结构体变量的成员作参数 ----值传递
用指向结构体变量或数组的指针作参数 ----地址传递
用结构体变量作参数 ----多值传递,效率低
struct data
{ int a,b,c; };
main()
{ void func(struct data);
struct data arg;
arg.a=27; arg.b=3; arg.c=arg.a+arg.b;
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
printf("Call Func()....\n");
func(arg);
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
}
void func(struct data parm)
{ printf("parm.a=%d parm.b=%d parm.c=%d\n",parm.a,parm.b,parm.c);
printf("Process...\n");
parm.a=18; parm.b=5; parm.c=parm.a*parm.b;
printf("parm.a=%d parm.b=%d parm.c=%d\n",parm.a,parm.b,parm.c);
printf("Return...\n");
}
arga,27b,3
c,30
(main)
(func)
parm
a,27
b,3
c,30
copy arg
:
,
c,
)
(func)
parm
a,18
b,5
c,90
arg
a 27
b,3
c,30
(main)
arg
a,27
b,3
c,30
例 用结构体变量作函数参数
struct data
{ int a,b,c; };
main()
{ void func(struct data *parm);
struct data arg;
arg.a=27; arg.b=3; arg.c=arg.a+arg.b;
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
printf("Call Func()....\n");
func(&arg);
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
}
void func(struct data *parm)
{ printf("parm->a=%d parm->b=%d parm->c=%d\n",parm->a,parm->b,parm->c);
printf("Process...\n");
parm->a=18; parm->b=5; parm->c=parm->a*parm->b;
printf("parm->a=%d parm->b=%d parm->c=%d\n",parm->a,parm->b,parm->c);
printf("Return...\n");
}
arg
a,18
b,5
c,90
(main)
arg
a,27
,3
c,30
ai )例 用结构体指针变量作函数参数
arga,27b,3
c,30
ai )
(func)
parm****
arga,18b,5
c,9
(main)
(f c)
10.7 链表
10.7.1 概述链表存储结构是一种动态数据结构,其特点是它包含的数据对象的个数及其相互关系可以按需要改变,存储空间是程序根据需要在程序运行过程中向系统申请获得,链表也不要求逻辑上相邻的元素在物理位置上也相邻,
它没有顺序存储结构所具有的弱点。
1.链表结构
( 1) 头指针变量 head──指向链表的首结点 。
( 2) 每个结点由 2个域组成:
1) 数据域 ──存储结点本身的信息 。
2) 指针域 ──指向后继结点的指针 。
( 3) 尾结点的指针域置为,NULL( 空 ),,
作为链表结束的标志
Qian Sun Li Zhou Wu Wang
Head 7 13 1 43 25 37
链表结构的定义
struct student
{char name[10];
struct student *next;
};
§ next为 struct student类型指针变量,指向下一个结点。
§ 结点的变量或指针变量的定义:
struct student node,*head;
node可以存放一个学生结点
指针 head可以存放学生结点的地址。
动态分配存储空间库函数
1,void *malloc(unsigned int size);
malloc在内存的动态存储区中分配一个 size长度的连续存储空间。
返值:返回一个指向分配域地址的指针(类型为
void) ;若未成功,则返回空指针( NULL)
例如:
int *p;
p=(int *)malloc(8);
p指示系统分配的 4个整型存储单元的起始地址
也可看成包含 4个数组元素的 p数组,p[0],p[1],p[2],p[3]
2.void *calloc(unsigned n,unsigned size);
§ 函数作用:在内存的动态存储区中分配 n
个长度为 size 的连续空间。
§ 返回值:一个指向分配域起始地址的指针;不成功返回 NULL。
§ 用 calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为 size。
2,void free(void *p);
§ 函数 free释放由指针变量 p所指示的内存区域。
例如,free(p);
通过函数 free将已分配的内存区域交还系统,
使系统可以重新对其进行分配。
【 例 10-12】 动态定义数组。
#include <stdio.h>
void main()
{int n,i,*p;
printf("n=");
scanf("%d",&n);
p=(int *)malloc(n*sizeof(int));
for(i=0;i<n;i++)
p[i]=i*i;
for(i=0;i<n;i++)
printf("%d ",p[i]);
free(p); }
程序运行结果:
n=10
0 1 4 9 16 25 36 49 64 81
对链表的基本操作链表的基本操作有:创建、查找、插入、删除和修改等。
(1) 创建链表,从无到有地建立起一个链表。
(2) 查找:按给定的结点索引号或检索条件,查找某个结点。如果找到指定的结点,则称为检索成功;
否则,称为检索失败。
(3) 插入:在结点 ki-1与 ki之间插入一个新的结点 k,
使表的长度增 1,且逻辑关系发生如下变化:
插入前,ki-1是 ki的前驱,ki是 ki-1的后继;
插入后,新插入的结点 k’成为 ki-1的后继,ki的前驱。
链表的删除操作
(4) 删除操作:删除结点 ki,使链表的长度减 1,
且 ki-1,ki和 ki+1结点之间的逻辑关系发生如下变化:
删除前,ki是 ki+1的前驱,ki-1的后继;
删除后,ki-1成为 ki+1的前驱,ki+1成为 ki-1的后继。
10.7.2 建立链表
1,尾插法建立单链表特点:头指针固定不变,新产生的结点总是链接到链表的尾部。
操作步骤:
( 1)设 head为链表头,last为链表尾结点,head=last=NULL;
( 2)生成新结点,由 p指针指示,并将新结点的地址域清空:
p->next=NULL;
( 3)如果 head为 NULL,则
head=p;
否则 last->next=p;
( 4) last=p;
( 5)重复( 2) ~( 4),继续建立新结点。
2,头插法建立单链表特点,新产生的结点作为新的链表头插入链表。
操作步骤:
( 1) head=NULL;
( 2)生成新结点,指针变量 p指向该结点;
( 3) p->next=head; head=p;
( 4)重复( 2) ~( 3),继续生成下一个链表结点。
10.7.3 链表的访问
1,输出链表结点操作步骤:
( 1)得到链表头结点的地址 head;
( 2)指针变量 p=head;
( 3)输出 p所指结点的成员值 ;
( 4) p后移一个结点,p=p->next;
( 5)重复( 3)( 4),直到链表为空。
2,统计链表结点的个数一般情况下,各个单链表中结点个数是随机的,
要想知道表中结点数目,必须从表头开始访问到表尾,逐个统计出结点数目。
3,查找链表的某个结点在链表上查找符合某个条件的结点,也必须从链表头开始访问链表。
10.7.4 链表的插入操作在第 n个结点之后插入 1个新结点,插入操作步骤:
( 1) q指针指向新结点,i为已访问过的结点数;
( 2) p=head,r指向 p结点的前一个结点;
( 3) i++,r=p,p=p->next,p结点往前移动一个结点;
( 4)若 i<n且 p!=NULL,则重复( 3)
( 5)若 i==0,则链表为空,没有结点,q结点作为链表的第 1个结点插入,q->next=head,head=q;
( 6)若 i<n且 p==NULL,则链表不足 n个,将 q结点插入到链表尾 r结点之后,r->next=q,q->next=NULL;
( 7)否则,将 q结点插入到第 n个结点之后,即插入到 r结点与 p结点之间,r->next=q,q->next=p;
( 8)返回链表头 head。
图 10-7 将指针 q所指结点插入第 n个结点之后
(b) 插入到第 2个结点之后
Head
p
101
Zhang
90
103
Wang
80
NU
L
L
105
Li
70
(a) head指示已有链表,q指示待插入结点
q 104
Zhao
70
Head
r
101
Zhang
90
q 104
Zhao
60
103
Wang
80
p
NU
L
L
105
Li
70
10.7.5 链表的删除操作删除第 n个结点
( 1) p=head,q指针指向 p所指结点的前 1个结点;
( 2) i为访问过的结点数目;
( 3) i++,q=p,p=p->next,p,q移动 1个结点;
( 4) 若 p!=NULL且 i<n-1,重复 ( 3)
( 5) 若 n==1,则删除第 1个结点,将下一个结点作为链表头结点:
head=head->next;
( 6) 若 head==NULL,链表为空,不能删除;
( 7) 若 p==NULL,第 n个结点不存在,不能删除;
( 8) 找到第 n个结点,删除 p结点:
q->next=p->next; p的前 1个结点的 next值赋值为 p的 next域;
( 9)返回 head。
图 10-9 删除第 n个结点
(a) head指示已有链表
Head
p
101
Zhang
90
103
Wang
80
NU
L
L
105
Li
60
101
Zhao
70
(b) 删除第 3个结点
q p
Head
101
Zhang
90
103
Wang
80
NU
L
L
105
Li
60
101
Zhao
70
§ 11.8 共用体
构造数据类型,也叫联合体
用途:使几个不同类型的变量共占一段内存 (相互覆盖 )
共用体类型定义定义形式:
union 共用体名
{
类型标识符 成员名;
类型标识符 成员名;
…………….
};例 union data
{ int i;
char ch;
float f;
}; f
ch
i 类型定义 不分配内存形式一,
union data
{ int i;
char ch;
float f;
}a,b;
形式二,
union data
{ int i;
char ch;
float f;
};
union data a,b,c,*p,d[3];
形式三,
union
{ int i;
char ch;
float f;
}a,b,c;
共用体变量的定义
f
ch
i
f
ch
i
a b
共用体 变量定义 分配内存,
长度 =最长成员 所占字节数共用体 变量任何时刻只有 一个成员 存在
共用体变量引用
引用方式:
例 a.i=1;
a.ch=?a?;
a.f=1.5;
printf(“%d”,a.i); (?编译通过,运行结果不对 )
引用规则
不能引用共用体变量,只能 引用其成员共用体指针名 ->成员名共用体变量名,成员名 (*共用体指针名 ).成员名
union data
{ int i;
char ch;
float f;
};
union data a,b,c,*p,d[3];
a.i a.ch a.f
p->i p->ch p->f
(*p).i (*p).ch (*p).f
d[0].i d[0].ch d[0].f
共用体变量中起作用的成员是 最后一次存放的成员例 union
{ int i;
char ch;
float f;
}a;
a=1; (?)
不能 在定义共用体变量时 初始化例 union
{ int i;
char ch;
float f;
}a={1,?a?,1.5}; (?)
可以用一个共用体变量为另一个变量赋值例 float x;
union
{ int i; char ch; float f;
}a,b;
a.i=1; a.ch=?a?; a.f=1.5;
b=a; (?)
x=a.f; (?)
例 将一个整数按字节输出
01100001 01000001
低字节高字节
01000001
01100001
ch[0]
ch[1]
运行结果:
i=60501
ch0=101,ch1=141
ch0=A,ch1=a
main()
{ union int_char
{ int i;
char ch[2];
}x;
x.i=24897;
printf("i=%o\n",x.i);
printf("ch0=%o,ch1=%o\n
ch0=%c,ch1=%c\n",
x.ch[0],x.ch[1],x.ch[0],x.ch[1]);
}
结构体与共用体
区别,存储方式不同
struct node
{ char ch[2];
int k;
}a;
union node
{ char ch[2];
int k;
}b;
achk
bch k
变量的各成员同时存在任一时刻只有一个成员存在
联系,两者可相互嵌套例 结构体中嵌套共用体
name num sex job class position
Li
Wang
1011
2086
F
M
S
T
501
prof
循环 n次读入姓名、号码、性别、职务
job==?s?真真 假假读入 class 读入
position
输出
“输入错”
循环 n次
job==?s?真 假输出,姓名,号码,
性别,职业,班级输出,姓名,号码,
性别,职业,职务
job==?t?
struct
{ int num;
char name[10];
char sex;
char job;
union
{ int class;
char position[10];
}category;
}person[2];
例共用体中嵌套结构体,机器字数据与字节数据的处理
00010010 00110100
低字节高字节
00110100
00010010
low
high
0x1234
00010010 11111111
低字节高字节
11111111
00010010
low
high
0x12ff
struct w_tag
{ char low;
char high;
};
union u_tag
{ struct w_tag byte_acc;
int word_acc;
}u_acc;
word_acc byte_acc.lowbyte_acc.high
u_acc
§ 11.10 用 typedef定义类型
功能,用自定义名字为 已有 数据类型命名
类型定义 简单形式,typedef type name;
例 typedef int INTEGER;类型定义语句关键字 已有数据类型名 用户定义的类型名例 typedef float REAL;
类型定义后,与已有类型一样使用例 INTEGER a,b,c;
REAL f1,f2;
int a,b,c;
float f1,f2; 说明,1.typedef 没有创造 新数据类型
2.typedef 是定义类型,不能定义变量
3.typedef 与 define 不同
define typedef
预编译时处理 编译时处理简单字符置换 为已有类型命名
typedef定义类型步骤
按定义变量方法先写出定义体 如 int i;
将变量名换成新类型名 如 int INTEGER;
最前面加 typedef 如 typedef int INTEGER;
用新类型名定义变量 如 INTEGER i,j;
例 定义数组类型
int a[100];
int ARRAY[100];
typedef int ARRAY[100];
ARRAY a,b,c;
int a[100],b[100],c[100];
例 定义指针类型
char *str;
char *STRING;
typedef char *STRING;
STRING p,s[10];
char *p;
char *s[10];
例 定义函数指针类型
int (*p)();
int (*POWER)();
typedef int (*POWER)();
POWER p1,p2;
int (*p1)(),(*p2)();
例 定义结构体类型
struct date
{ int month;
int day;
int year;
}d;
例 定义结构体类型
struct date
{ int month;
int day;
int year;
}DATE;
例 定义结构体类型
typedef struct date
{ int month;
int day;
int year;
}DATE;
例 定义结构体类型
DATE birthday,*p;
struct date
{ int month;
int day;
int year;
}birthday,*p;
类型定义可嵌套例 typedef struct club
{ char name[20];
int size;
int year;
}GROUP;
typedef GROUP *PG;
PG pclub;? GROUP *pclub;? struct club *pclub;
GROUP为结构体类型
PG为指向 GROUP的指针类型