400 likes | 540 Views
第十一章 结构体与共用体. 11.1 结构体 结构体是 一种 构造 数据类型 用途:把 不同类型 的数据组合成一个整体------- 自定义 数据类型 结构体类型定义. 合法标识符 可省 : 无名结构体. struct [ 结构体名 ] { 类型标识符 成员名; 类型标识符 成员名; ……………. } ;. struct 是 关键字 , 不能省略. 成员类型可以是 基本型或构造型. 2 字节. num. …. name. 20 字节. 1 字节. sex. 2 字节. age. 4 字节. score.
E N D
第十一章 结构体与共用体 • 11.1结构体 • 结构体是一种构造数据类型 • 用途:把不同类型的数据组合成一个整体-------自定义数据类型 • 结构体类型定义 合法标识符 可省:无名结构体 struct [结构体名] { 类型标识符 成员名; 类型标识符 成员名; ……………. }; struct是关键字, 不能省略 成员类型可以是 基本型或构造型
2字节 num … name 20字节 1字节 sex 2字节 age 4字节 score ….. addr 30字节 例 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }; 结构体类型定义的作用域 结构体类型定义描述结构 的组织形式,不分配内存
struct 结构体名 { 类型标识符 成员名; 类型标识符 成员名; ……………. }; struct 结构体名变量名表列; • 11.2结构体变量的定义 • 先定义结构体类型,再定义结构体变量 • 一般形式: 例 #define STUDENT struct student STUDENT { int num; char name[20]; char sex; int age; float score; char addr[30]; }; STUDENT stu1,stu2; 例 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }; struct 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; 例 struct student { int num; char name[20]; struct date { int month; int day; int year; }birthday; }stu; birthday birthday num num name name month month day day year year • 说明 • 结构体类型与结构体变量概念不同 • 类型:不分配内存;变量:分配内存 • 类型:不能赋值、存取、运算;变量:可以 • 结构体可嵌套 • 结构体成员名与程序中变量名可相同,不会混淆 • 结构体类型及变量的作用域与生存期
例 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }stu1,stu2; stu1.num=10; 例 struct student { int num; char name[20]; struct date { int month; int day; int year; }birthday; }stu1,stu2; 例 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }stu1,stu2; stu1.score=85.5; stu1.birthday.month=12; stu1.score+=stu2.score; stu1.age++; stu2=stu1; ( ) 例 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }stu1,stu2; 例 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }stu1,stu2; birthday num name month day year if(stu1==stu2) …….. () printf(“%d,%s,%c,%d,%f,%s\n”,stu1); () stu1={101,“Wan Lin”,‘M’,19,87.5,“DaLian”}; () 引用方式: 结构体变量名.成员名 • 11.3结构体变量的引用 • 引用规则 • 结构体变量不能整体引用,只能引用变量成员 • 可以将一个结构体变量赋值给另一个结构体变量 • 结构体嵌套时逐级引用 成员(分量)运算符 优先级: 1 结合性:从左向右
struct 结构体名 { 类型标识符 成员名; 类型标识符 成员名; ……………. }; struct 结构体名结构体变量={初始数据}; • 11.4结构体变量的初始化 • 形式一: 例 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”};
num num name name 25B stu[0] sex sex age age stu[1] 形式一: struct student { int num; char name[20]; char sex; int age; }; struct student stu[2]; • 11.5结构体数组 • 结构体数组的定义 三种形式: 形式二: struct student { int num; char name[20]; char sex; int age; }stu[2]; 形式三: struct { int num; char name[20]; char sex; int age; }stu[2];
stu[1].age++; 分行初始化: 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; char name[20]; char sex; int age; }str[3]; strcpy(stu[0].name,”ZhaoDa”); 全部初始化时维数可省 • 结构体数组引用 顺序初始化: 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; char name[20]; char sex; int age; }stu[ ]={{……},{……},{……}}; • 结构体数组初始化 例 struct { int num; char name[20]; char sex; int age; }stu[ ]={{……},{……},{……}};
name count 0 Li 0 Zhang 0 Wang 例 统计后选人选票 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); }
计算学生的平均成绩和不及格的人数 struct stu{ int num; char *name; char sex; float score;}boy[5]={ {101,"Li ping",'M',45}, {102,"Zhang ping",'M',62.5}, {103,"He fang",'F',92.5}, {104,"Cheng ling",'F',87}, {105,"Wang ming",'M',58} };main(){ int i,c=0; float ave,s=0; for(i=0;i<5;i++) { s+=boy[i].score; if(boy[i].score<60) c+=1; } printf("s=%f\n",s); ave=s/5; printf("average=%f\ncount=%d\n",ave,c);}
建立同学通讯录 #include"stdio.h"#define NUM 3struct mem{ char name[20]; char phone[10];};main(){ struct mem man[NUM]; int i; for(i=0;i<NUM;i++) { printf("input name:\n"); gets(man[i].name); printf("input phone:\n"); gets(man[i].phone); } printf("name\t\t\tphone\n\n"); for(i=0;i<NUM;i++) printf("%s\t\t\t%s\n",man[i].name,man[i].phone);}
(*结构体指针名).成员名 结构体指针名->成员名 结构体变量名.成员名 p num name struct student { int num; char name[20]; char sex; int age; }stu; struct student *p=&stu; stu sex age 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); } • 11.6结构体和指针 • 指向结构体变量的指针 • 定义形式:struct 结构体名*结构体指针名; 例 struct student *p; • 使用结构体指针变量引用成员形式 存放结构体变量在内存的起始地址 struct student stu1; struct student *p=&stu1; stu1.num=101; (*p).num=101 例 int n; int *p=&n; *p=10; n=10 例 指向结构体的指针变量 指向运算符 优先级: 1 结合方向:从左向右
p num name stu[0] sex age p+1 stu[1] stu[2] 例 指向结构体数组的指针 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); } • 指向结构体数组的指针
用指向结构体的指针作函数参数 • 用结构体变量的成员作参数----值传递 • 用指向结构体变量或数组的指针作参数----地址传递 • 用结构体变量作参数----多值传递,效率低
(main) (main) copy (main) a :27 a :27 a :27 a :27 a :27 a :18 arg arg arg b: 3 b: 3 b: 3 b: 3 b: 3 b: 5 (main) c :30 c :30 c :30 c :90 c :30 c :30 arg (func) (func) parm parm 例 用结构体变量作函数参数 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"); }
(main) (main) (main) a :27 a :18 a :18 a :27 (func) (func) arg arg arg b: 5 b: 3 b: 3 b: 5 (main) parm parm **** **** c :30 c :90 c :30 c :90 arg 例 用结构体指针变量作函数参数 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"); }
11.7 链表处理──结构指针的应用 11.7.1 概述 1.链表结构 链表作为一种常用的、能够实现动态存储分配的数据结构,在《数据结构》课程中有详细介绍。 (1)头指针变量head──指向链表的首结点。 (2)每个结点由2个域组成: 数据域──存储结点本身的信息。 指针域──指向后继结点的指针。 (3)尾结点的指针域置为“NULL(空)”,作为链表结束的标志。
2.对链表的基本操作 对链表的基本操作有:创建、检索(查找)、插入、删除和修改等。 (1)创建链表是指,从无到有地建立起一个链表,即往空链表中依次插入若干结点,并保持结点之间的前驱和后继关系。 (2)检索操作是指,按给定的结点索引号或检索条件,查找某个结点。如果找到指定的结点,则称为检索成功;否则,称为检索失败。 (3)插入操作是指,在结点ki-1与ki之间插入一个新的结点k’,使线性表的长度增1,且ki-1与ki的逻辑关系发生如下变化:插入前,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的后继。
3.C语言对链表结点的结构描述在C语言中,用结构类型来描述结点结构。例如:struct grade { char no[7]; /*学号*/ int score; /*成绩*/ struct grade *next; /*指针域*/ };
#include <stdio.h> struct ss { int data; struct ss * link; }x,y,z,*head; main() { head=&x; x.link=&y; y.link=&z; z.link=NULL; x.link=y.link; free(y);} x y z head x y z
11.7.2 创建一个新链表 编写一个create()函数,按照规定的结点结构,创建一个单链表(链表中的结点个数不限)。 基本思路: 首先向系统申请一个结点的空间,然后输入结点数据域的(2个)数据项,并将指针域置为空(链尾标志),最后将新结点插入到链表尾。对于链表的第一个结点,还要设置头指针变量。 另外,3个指针变量head、new和tail的说明如下: (1)head──头指针变量,指向链表的第一个结点,用作函数返回值。 (2)new──指向新申请的结点。 (3)tail──指向链表的尾结点,用tail->next=new,实现将新申请的结点,插入到链表尾,使之成为新的尾结点。
#define NULL 0 #define LEN sizeof(struct grade)/*定义结点长度*/ struct grade /*定义结点结构*/ { char no[7]; /*学号*/ int score; /*成绩*/ struct grade *next; /*指针域*/ }; struct grade *create( void ) /*创建一个单链表*/ { struct grade *head=NULL, *new, *tail; int count=0; /*链表中的结点个数(初值为0)*/ for( ; ; ) /*缺省3个表达式的for语句*/ { new=(struct grade *)malloc(LEN); /*申请一个新结点的空间*/ printf("Input the number of student No.%d(6 bytes): ", count+1); scanf("%6s", new->no);
if(strcmp(new->no,"000000")==0) /*如果学号为6个0,则退出*/ { free(new); /*释放最后申请的结点空间*/ break; /*结束for语句*/ } printf("Input the score of the student No.%d: ", count+1); scanf("%d", &new->score); count++; /*结点个数加1*/ new->next=NULL; /*置新结点的指针域为空*/ if(count==1) head=new; /*是第一个结点, 置头指针*/ else tail->next=new; /*非首结点, 将新结点插入到链表尾*/ tail=new; /*设置新的尾结点*/ } return(head); }
11.7.3 对链表的插入操作 编写一个insert()函数,完成在单链表的第i个结点后插入1个新结点的操作。当i=0时,表示新结点插入到第一个结点之前,成为链表新的首结点。 基本思路: 通过单链表的头指针,首先找到链表的第一个结点;然后顺着结点的指针域找到第i个结点,最后将新结点插入到第i个结点之后。 函数参数:head为单链表的头指针,new指向要插入的新结点,i为结点索引号
i ch f • 11.8共用体 • 构造数据类型,也叫联合体 • 用途:使几个不同类型的变量共占一段内存(相互覆盖) • 共用体类型定义 定义形式: union 共用体名 { 类型标识符 成员名; 类型标识符 成员名; ……………. }; 例 union data { int i; char ch; float f; }; 类型定义不分配内存
i i ch ch f f a b • 共用体变量的定义 形式一: 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; 共用体变量任何时刻 只有一个成员存在 共用体变量定义分配内存, 长度=最长成员所占字节数
共用体变量名.成员名 共用体指针名->成员名 (*共用体指针名).成员名 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; () • 不能在定义共用体变量时初始化 例 a.i=1; a.ch=‘a’; a.f=1.5; printf(“%d”,a.i); (编译通过,运行结果不对) • 可以用一个共用体变量为另一个变量赋值 例 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 ch[0] 01000001 ch[1] 01100001 例 将一个整数按字节输出 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]); } 运行结果: i=60501 ch0=101,ch1=141 ch0=A,ch1=a
变量的各成员同时存在 struct node { char ch[2]; int k; }a; ch a k ch b k union node { char ch[2]; int k; }b; 任一时刻只有一个成员存在 • 结构体与共用体 • 区别: 存储方式不同 • 联系: 两者可相互嵌套
class 循环n次 name num sex job position 读入姓名、号码、性别、职务 Li 1011 F S 501 Wang 2086 job==‘s’ M T prof 真 假 job==‘t’ 真 假 读入class 读入 position 输出 “输入错” 循环n次 job==‘s’ 真 假 输出:姓名,号码, 性别,职业,职务 输出:姓名,号码, 性别,职业,班级 例 结构体中嵌套共用体 struct { int num; char name[10]; char sex; char job; union { int class; char position[10]; }category; }person[2];
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[i].job); if(person[i].job=='s') scanf("%d",&person[i].category.class); else if(person[i].job=='t') scanf("%s",person[i].category.position); else printf("Input error!"); } printf("\n 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.class); else printf("%-6d%-10s%-3c%-3c%-6s\n",person[i].num, person[i].name,person[i].sex,person[i].job,person[i].category.position); } }
共用体类型数据的特点 (1)系统采用覆盖技术,实现共用变量各成员的内存共享,所以在某一时刻,存放的和起作用的是最后一次存入的成员值。 例如,执行un1.i=1, un1.ch='c', un1.f=3.14后,un1.f才是有效的成员。 (2)由于所有成员共享同一内存空间,故共用变量与其各成员的地址相同。 例如,&un1=&un1.i=&un1.ch=&un1.f。 (3)不能对共用变量进行初始化(注意:结构变量可以);也不能将共用变量作为函数参数,以及使函数返回一个共用数据,但可以使用指向共用变量的指针。 (4)共用类型可以出现在结构类型定义中,反之亦然。
int a,b,c; float f1,f2; • 11.10用typedef定义类型 • 功能:用自定义名字为已有数据类型命名 • 类型定义简单形式: typedef type name; 例 INTEGER a,b,c; REAL f1,f2; 例 typedef int INTEGER; 用户定义的类型名 已有数据类型名 类型定义语句关键字 例 typedef float REAL; 类型定义后,与已有类型一样使用 说明: 1.typedef 没有创造新数据类型 2.typedef 是定义类型,不能定义变量 3.typedef 与 define 不同 definetypedef 预编译时处理编译时处理 简单字符置换 为已有类型命名
typedef定义类型步骤 • 按定义变量方法先写出定义体 如 int i; • 将变量名换成新类型名 如 int INTEGER; • 最前面加typedef 如 typedef int INTEGER; • 用新类型名定义变量 如 INTEGER i,j; • 类型定义可嵌套 例 定义结构体类型 • 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; 例 定义函数指针类型 • int (*p)(); • int (*POWER)(); • typedef int (*POWER)(); • POWER p1,p2; 例 定义数组类型 • int a[100]; • int ARRAY[100]; • typedef int ARRAY[100]; • ARRAY a,b,c; 例 定义结构体类型 • DATE birthday, *p; 例 定义指针类型 • char *str; • char *STRING; • typedef char *STRING; • STRING p,s[10]; 例 typedef struct club { char name[20]; int size; int year; }GROUP; typedef GROUP *PG; PG pclub; GROUP为结构体类型 PG为指向GROUP的指针类型 struct date { int month; int day; int year; }birthday, *p; int (*p1)(),(*p2)(); char *p; char *s[10]; int a[100],b[100],c[100]; GROUP *pclub; struct club *pclub;
本章小结 • 结构和共用是两种构造类型数据,是用户定义新数据类型的重要手段。结构和共用有很多的相似之处,它们都由成员组成。成员可以具有不同的数据类型。成员的表示方法相同。都可用三种方式作变量说明。 • 在结构中,各成员都占有自己的内存空间,它们是同时存在的。一个结构变量的总长度等于所有成员长度之和。在联合中,所有成员不能同时占用它的内存空间,它们不能同时存在。共用变量的长度等于最长的成员的长度。 • “.”是成员运算符,可用它表示成员项,成员还可用“->”运算符来表示。 • 结构变量可以作为函数参数,函数也可返回指向结构的指针变量。而共用变量不能作为函数参数,函数也不能返回指向共用的指针变量。但可以使用指向共用变量的指针,也可使用共用数组。 • 结构定义允许嵌套,结构中也可用共用作为成员,形成结构和共用的嵌套。
常见错误列举 1、定义结构体、共用体或枚举类型时,在}后漏掉了分号。 2、定义复杂数据类型时为成员变量赋初值。 3、结构体中含有自身类型的成员。 4、对结构体变量进行整体赋值。 5、typedef语句漏掉分号。 6、把一种类型的结构变量赋值给另一个结构变量。 7、比较结构体或共用体变量的大小。 8、在-〉中间插入空格。 9、试图只用成员名引用结构体成员。 10、在定义共用体变量时,对其赋初值。