第十四章 结构体、共用体和用户定义类型概述,用户可以构造的三种数据类型三种形式,
1.用户定义类型 (typedef)
2.结构体 (struct)
3.共用体 (union)
第十四章 结构体、共用体和用户定义类型
14.1 用 typedef说明一种新类型名形式,typedef 类型名 标识名 ;
如,typedef int INTEGER;
它把一个用户命名的标识符 INTEGER说明成一个 int类型的类型名即,INTEGER是 int的一个别名习惯上,把新的类型名用大写字母表示定义步骤,1.按通常方法定出定义的主体
char *p;
2.将变量名换成新类型名,
char *CHARP;
3.加上关键字 typedef:
typedef char *CHARP;(声明
CHARP为字符指针类型 )
4.用新类型名定义变量,
CHARP p;(p为字符指针变量 )
引言,有些问题仅用基本类型和数组来描述是无法实现的,需要将不同类型的数据组合成一个有机的整体。 因此,当要把一些相关信息组合在一起时,利用结构体类型很方便,
如学生情况表,
姓名 (name):字符串性别 (sex):字符型出生日期,(birthday):data结构体四门课成绩,(sc):一维实型数组
14.2 结构体类型我们可以将上述四个成员组成一个名为
student的整体,这些数据之间有相互关连的关系,不能拆开处理,
14.2.1 结构体类型的说明一般形式,
struct 结构体标识名
{ 类型名 1 结构成员表 1;
类型名 2 结构成员表 2;
类型名 n 结构成员表 n;
};
struct student
{ char name[10];
char sex;
struct
{ int year;
int month;
int day;
}birthday;
float sc[4];
};
说明,
1,关键字 struct 和,;”不能少。
2,类型名 1~类型名 n可以是简单类型,也可以是构造类型,允许结构体嵌套。
3 这是用户自定义的类型,一经声明,就可以和其它基本类型一样用来定义这种类型的变量了。
4,以上的说明仅列出了该结构的组成情况,标志这种类型的结构,模式“存在。 但没有分配存储空间。
14.2.2 结构体类型的变量,数组和指针变量的定义可以有四种方法定义变量第一种形式,
struct student
{ char name[10];
char sex;
struct date birthday;
float sc[4];
} std,pers[3],*pstd;
变量 std的结构如下,
n s y m d 0 1 2 3
具有这一结构类型的变量中只能存放一组数据 (一个学生的档案 )
数组 pers,可以存放三名学生的档案
pstd:指向具有 struct student 类型的存储单元第二种形式,直接定义
struct
{
……
} std,pers[3],*pstd;
第三种形式,先说明结构体类型,再单独进行变量定义
struct student
{
……
};
struct student std,pers[3],*pstd;
注意,struct student 必须放在一起第四种形式,用 typedef说明一个结构体类型名,
再用新类型名来定义变量,
typedef struct
{
……
}STREC;
STREC std,pers[3],*pstd;
14.2.3 给结构体变量,数组赋初值结构体变量和数组也可以在定义的同时赋初值
1.给结构体变量赋初值
struct student
{ char name[10];
char sex;
struct date birthday;
float sc[4];
}std={“Li Ming”,?M?,1962,5,10,88,76,85.5,90};
注意:按 顺 序一一对应赋初值可以给前面的成员赋初值,后面一般自动赋初值 零
2.给结构体数组赋初值
struct bookcard
{ char num[5];
float money;
}
bk[3]={{“NO.1”,35.5},{“NO.2”,25.0},{“NO.3”,66.7}};
14.2.4 引用结构体变量中的数据
1.对结构成员的引用
(1)结构体变量名,成员名
(2)指针变量名 ─>成员名
(3)(*指针变量名 ).成员名 (注意一对圆括号不能少 ))
struct student
{ char name[10];
char sex;
struct date birthday;
float sc[4];
}std,arr[5],*ps;
ps=&std;
引用分如下几种情况,
(1) 若要引用结构体变量 std中的 sex成员
std.sex
ps ─ >sex
(*ps).sex
(2)若要引用结构体数组 arr的第 0个元素 arr[0]中的
sex成员
arr[0].sex (注意不能写成 arr.sex)
(3)若要引用结构体变量 std中的数组成员 sc中的元素 sc[2]时
std.sc[2]或 ps ─ >sc[2]或 (*ps).sc[2]
(4)若结构体成员是字符型数组,如 name,引用如下,
std.name 或 ps ─ >name或 (*ps).name
(5)内嵌结构体成员引用如下,
std.birthday.year或 ps ─ >birthday.year或
(*ps).birthday.year
或 arr[0].birthday.year
注意,birthday后面不能使用 ─ >运算符,因为
birthday不是指针变量
2.对结构体变量中的成员进行操作对结构体变量中的每个成员,可以象同类变量一样操作
(1)对字符数组成员操作如,
scanf(“%s”,std.name);或 gets(std.name)
pstd=&std; scanf(“%s”,pstd ─ >name); 或
gets(std.name)
for(i=0;i<3;i++) scanf(“%s”,pers[i].name);
strcpy(std.name.”Li Ming”);
(注不可写成 std.name=,Li Ming”);
(2)对字符成员操作如,
scanf(“%c”,&std.sex)或 std.sex=getchar();
pstd=&std;
scanf(“%c”,&pstd ─ >sex),或 std.sex=getchar();
for(i=0;i<3;i++) scanf(“%c”,&pers[i].sex);
std.sex=?M?;
(3)对结构体成员中的成员操作
scanf(“%d”,&std.birthday.year);
pstd=&std; scanf(“%d”,&pstd ─ >birthday.year);
for(i=0;i<3;i++)
scanf(“%d”,&pers[i].birthday.year);
std.birthday.year=1962;
(4)对结构体成员中的成员数组操作
for(j=0;j<5;j++) scanf(“%f”,&std.sc[j]);
pstd=&std;
for(j=0;j<5;j++) scanf(“%f”,&pstd ─ >sc[j]);
for(i=0;i<3;i++)
for(j=0;j<5;j++)
{scanf((“%f”,&std.sc[j]);
pers[i].sc[j]=std.sc[j];
}
3.引用结构体成员注意优先级如,struct
{ int a;
char *s;
}*p;
则如下表达式的含义各不相同
(1) ++p->a 相当于 ++(p ->a )(结果 a增 1,而非 p增 1)
因为 -->优先级高于 ++.
(2) (++P) ->a相当于 P++ ->a (结果 p增 1)
(3) *p->s 它是引用 s所指的存储单元
(4) *p->s ++它是引用 s所指的存储单元之后,使指针 s
增 1
(5) (*p->s) ++它是引用 s所指的存储单元内容增 1
(6) *p ++ ->s在访问了 s所指存储单元后,使 p增 1
4.相同类型结构休变量之间的整体赋值
Struct
{

}per1,per2={“YANGGM”,46};
则 per1=per2后,per2中每个成员的值都赋给了 per1
中对应的同名成员,
14.2.5函数之间结构体变量的数据传递
1.向函数传递结构体变量的成员结构体变量中的每个成员可以是一般 变量,
数组或指针变量,作为指针变量可作允许的任何,包括参数传递
2.向函数传递结构体变量,即把结构变量作为一个整体传递给相应的形参,,这时传递的是实参结构 体变量的值,函数体内对形参结构体变量中任何成员的操作并不影响对应实参中成员的值 (即单向传递 )
3.传递结构体的地址允许将结构体变量的地址作为实参传递,对应的形参应是一个基类型相同的结构体的指针例 14.1见书这 95
4.函数的返回值是结构体类型的值例 14.2见书这 96
5.函数的返回值可以是指向结构体变量的值例 14.3见书这 96
例 14.4读入五位用户的姓名和电话号码,按姓名的字典顺序排列后,输出用户的姓名和电话号码,
见书 P196
14.2.6利用结构体变量构成链表
1.结构体中含有可以指向本结构体的指针成员当一个结构体中有一个或多个成员是指针,它的基类型即是本结构体类型时,称为“引用自身的结构体如,struct link
{ char ch;
struct link *p;
} a;
P是一个可以指向 struct link类型的指针
a.p=&a是合法的表达式构成的存储结构如下图
a.ch a,p
例 14.5 一个简单的链表
struct node
{ int data;
struct node *next;
};
typedef struct node NODETYPE;
main()
{ NODETYPE a,b,c,*h,*p;
a.data=10; b.data=20; c.data=30;
h=&a;
a.next=&b;
b.next=&c;
c.next=?\0?;
p=h;
while(p)
{printf(“%d”,p->data);
p=p->next;}
printf(“\n”); }
h a b c
10 20 30 ^
2.动态链表的概念前面我们都是利用数组来存储批量数据,定义数组必须指明数组的大小,这样限定了存放的数据量,
但是,一个程序在每次运行时要处理数据的数量并不确定,若还用数组,就会造成大小不合适,
为了较合理地使用存储空间,可以使用动态存储分配,动态存储分配的存储单元,地址是不连续的,各数据之间存在接序关系,可以用链表这样的存储结构来反映数据之间的相互关系,
在这种链表的每个结点中,需要存放数据本身的数据域外,至少还需要一个指针域来存放下一个结点的地址,从而通过这些指针接起来,
形成图 14.5 的链表
9 8 7 ^head
头指针 头结点
///
带有头结点的单向链表这类链表只能从当前结点找到后继结点,
故称为,单向链表,
3.单向链表为了构成上图所示的单向链表,每个结点应有两个成员组成,结点的定义如下,
struct slist
{ int data;
struct slist *next;
} ;
typedef struct slist SLIST;
单向链表有关的算法,
(1) 建立带有头结点的单向链表步骤,① 读取数据 ; ② 生成新结点
③将数据存入结点的成员变量中
④将新结点插入到链表中例 14.6 编写函数 creat_slist1,建立图 14,5所示的单向链表,
SLIST *creat_slist1()
{ int c;
SLIST *h,*s,*r;
h=(SLIST*)malloc(sizeof(SLIST));
r=h;
scanf(“%d”,&c);
while(c!= -1)
{ s=(SLIST*)malloc(sizeof(SLIST));
s->data=c;
r->next=s;
r=s;
scanf(“%d”,&c);
}
r->next=?\0?;
return h;}
main()
{SLIST *head;
…,
head=creat_slist1(); ………,}
(2) 顺序访问链表中各结点的数据域例 14.7 见书 P201
(3)在单向链表中插入结点
p q
s
9 8
0
insert_snode(SLIST *head,int x,int y)
{ SLIST *s,*p,*q;
s=(SLIST *)malloc(sizeof(SLIST));
s->data=y;
q=head; p=head->next;
while((p!=?\0?)&&(p->data!=x))
{ q=p; p=p->next;}
s->next=p; q->next=s;
}
(4) 删除单向链表的结点
head 9 8 7
头指针 头结点
///
q p
单链表结点的删除
14.3 共用体共用体与结构体的 类型说明 和 定义的方法 完全相同不同点,结构体变量中的成员各自占有自己的空间,而共用体变量中的所有成员点有同一存储空间,
14.3.1共用体类型的说明和变量的定义
1.形式,union 共用体标识符
{ 类型名 1 共用体成员 1;
类型名 2 共用体成员 2;
类型名 n 共用体成员 n; };
2.共用体变量的定义
union un_1
{ int i;
float x;
} s1,s2,*p;
变量 s1的存储空间如图 14.9所示
2个字节高位
2个字节低位
S1.i
S1.x
说明,
结构体,变量所占内存字节数等于所有成员所占字节数的总和共用体,变量所占内存字节数与其成员中占字节数最多的那个成员相等,
共用体,因为所有成员共享存储空间,变量中的所有成员的首地址相同,
即,&s1==&s1.x==&s1.x.
14.3.2 共用体变量的引用
1.成员的引用
(1) 共用体变量名,成员名
(2) 指针变量名 ->成员名
(3) (*指针变量名 ).成员名注意,共用体变量中起作用的是最近一次存入的成员变量值,原有成员变量的值被覆盖,
如,s1.x=123.4;
s1.i=100;
printf(“%f\n”,s1.x);
2,共用体变量的整体赋值若有,s1.i=5,
则,s2=s1;
printf(“%d\n”,s2.i); 输出的值为 5
3,向函数传递共用体变量的值共用体类型的变量可以作为实参进行传递,
也可以传递共用体变量的地址,
例 14.9 利用共用体的特点分别取出 int变量中高字节和低字节的两个数
union change
{ char c[2];
int a;
} un;
main()
{un.a=16961;
printf(“%d,%c\n”,un.c[0],un.c[0])
printf(“%d,%c\n”,un.c[1],un.c[1]);
}