400 likes | 501 Vues
第九章 结构体与共用体. 9.1 结构体 结构体是 一种 构造 数据类型 用途:把 不同类型 的数据组合成一个整体------- 自定义 数据类型 结构体类型定义. 合法标识符 可省 : 无名结构体. struct [ 结构体名 ] { 类型标识符 成员名; 类型标识符 成员名; ……………. } ;. 成员类型可以是 基本型或构造型. struct 是 关键字 , 不能省略. 2 字节. num. …. name. 20 字节. 1 字节. sex. 2 字节. age. 4 字节. score.
E N D
第九章 结构体与共用体 • 9.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 结构体名 变量名表列; • 9.2.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; 例 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; printf(“%d,%s,%c,%d,%f,%s\n”,stu1); () 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; birthday num name month day year if(stu1==stu2) …….. () 引用方式: 结构体变量名.成员名 • 9.2.3结构体变量的引用 • 引用规则 • 结构体变量不能整体引用,只能引用变量成员 • 可以将一个结构体变量赋值给另一个结构体变量 • 结构体嵌套时逐级引用 成员(分量)运算符 优先级: 1 结合性:从左向右
struct 结构体名 { 类型标识符 成员名; 类型标识符 成员名; ……………. }; struct 结构体名 结构体变量={初始数据}; • 9.2.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] • 9.3结构体数组 • 结构体数组的定义 三种形式: 形式一: struct student { int num; char name[20]; char sex; int age; }; struct student stu[2]; 形式二: 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, "ZhaoDan”); 全部初始化时维数可省 • 结构体数组引用 顺序初始化: 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[ ]={{……},{……},{……}};
例1:计算学生的平均成绩和不及格人数 struct student { int num; char name[20]; float score; }student1[5] = { {1001, "Li ping",55}, {1002, "Zhang ping", 80}, {1003, "Wang fang", 75}, {1004, "Cheng lin", 82}, {1005, "Wu yong", 94}}; main() { int i , c = 0; float average, s=0; for(i=0 ; i<5 ; i++) { s += student1[i].score; if(student1[i]<60) c+=1; } average = s/5 ; printf(" average=%f\n count=%d\n ",average, c); }
例2:建立同学通讯录 printf("input phone: "); gets(man[i].phone); } printf("name\t\tphone\n\n"); for (i=0; i<NUM; i++) printf("%s\t\t%s\n",man[i].name, man[i].phone); } #include "stdio.h" #define NUM 3 struct mem { char name[20]; char phone[10]; }; main() { struct mem man[NUM]; int i; for(i=0 ; i<NUM ; i++) { printf(" input name: "); gets(man[i].name);
p num name struct student { int num; char name[20]; char sex; int age; }stu; struct student *p=&stu; stu sex 例 int n; int *p=&n; *p=10; n=10 struct student stu1; struct student *p=&stu1; stu1.num=101; (*p).num=101 age (*结构体指针名).成员名 结构体指针名->成员名 结构体变量名.成员名 例3:指向结构体变量的指针的应用 struct student { int num; char name[20]; float score; }student1 = {1001, "Zhang", 75.5}, *spoint; main() { spoint = &student1; printf("Number = %d, Name = %s, ",student1.num, student1.name); printf("Score = %f\n",student1.score); printf("Number = %d, Name = %s, ",(*spoint).num, (*spoint).name); printf("Score = %f\n",(*spoint).score); printf(" Number = %d, Name = %s, ",spoint->num, spoint->name); printf("Score = %f\n ,spoint->score); } • 9.4结构体和指针 • 指向结构体变量的指针 • 定义形式:struct 结构体名 *结构体指针名; 例 struct student *p; • 使用结构体指针变量引用成员形式 存放结构体变量在内存的起始地址 例 指向结构体的指针变量 指向运算符 优先级: 1 结合方向:从左向右
p num name stu[0] sex age p+1 stu[1] stu[2] 例3: 指向结构体数组的指针 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); } • 指向结构体数组的指针
9.5 链表 9.5.1 动态存储分配和链表的概念 由程序员控制的存储分配方法,根据需要临时分配内存单元以存放有用数据,当不用时,又可以释放存储单元,以便其用于分配给其它数据使用。 动态存储分配: 链表: 链表是动态进行存储分配的一种结构。若干数据(每个数据组称为一个结点)按一定的原则连接起来。
head 1249 1356 1475 1021 1249 A B C D 1356 1475 1021 NULL 简单的链表: • 链表中每一个元素称为一个结点,结点是一组数据,包括用户需要的实际数据和下一个结点的地址。 • 前一个结点指向下一个结点,只有通过前一个结点才能找到下一个结点。 • 设置一指针变量,存放第一个结点的地址,称为头指针,一般以head命名。 • 最后一个结点的地址项不指向任何结点,赋以值NULL。
定义结点: struct student {int num; float score; struct student *next; }; 用c语言实现链表结构: 包含指针项的结构体就是一个结点。 num score next 4016 3028 3010 head 99103 99107 99101 3010 90 85 89.5 NULL 4016 3028 A B C
定义结点: struct student {long num; float score; struct student *next; }a,b,c,*head; 例 建立一个简单链表 赋予有用数据: a.num=99101; a.score=89.5; b.num=99103; b.score=90; c.num=99107; c.score=85; 建立链表: head=&a; a.next=&b;b.next=&c; c.next=0;
9.5.2 用于动态链表的函数 • malloc函数 • 作用: • C标准函数库中动态存储分配的函数: malloc( ) calloc( ) 在内存中开辟指定大小的存储空间,并将此存储空间的起始地址作为函数返回值. • malloc函数的使用: malloc(8)开辟长度为8个字节的存储空间,若其起始地址为1268,则malloc(8)的返回值为1268,且返回的指针值指向void型.将此地址赋给一个指向long型的指针变量: p=(long *)malloc(8); 开辟一个结点: malloc(sizeof(struct student));
函数的作用: • calloc函数: 分配num个大小为size字节的空间,函数返回值为该空间的起始地址. • 函数的使用: calloc(10,20) 开辟10个大小为20字节的空间,函数返回该空间的起始地址.
链表应用 链表所占内存区大小不固定,各结点在内存中的次序可以是任意的. 链表每一个结点内必须包括一个指针项,指向下一结点. • 链表的特点: 必须用malloc和calloc函数动态开辟单元,且开辟的结点只能用指针方法访问. 单向链表的访问只能从头指针开始,顺序进行. 单向链表最后一个结点的指针项必须是NULL. 若断开链表中某一个结点,其后的所有结点虽在内存中存在,但无法访问.
例 建立一个链表存放学生信息--建立链表 #define LEN sizeof (struct stu) struct stu { int num; int age; struct stu *next; }; struct stu *creat(int n) { struct stu *head,*pf,*pb; int i; for(i=0;i<n;i++) { pb=( struct stu *) malloc(LEN); printf("input Number and Age\n"); scanf("%d%d",&pb->num,&pb->age); if(i==0) pf=head=pb; else pf->next=pb; pb->next=NULL; pf=pb; } return(head); }
例 查找与输出链表中的数据 struct stu * search (struct stu *head , int n) { struct stu *p; int i; p=head; while (p->num!=n && p->next!=NULL) p=p->next; /* 不是要找的结点后移一步*/ if (p->num==n) return (p); if (p->num!=n&& p->next==NULL) printf ("Node %d has not been found!\n", n); }
例在链表中插入一个结点 if(pi->num<=pb->num) { if (head ==pb) head=pi; /*在第一结点之前插入*/ else pf->next=pi; /*在其它位置插入*/ pi->next=pb; } else { pb->next=pi; pi->next=NULL; } /*在表末插入*/ } return head; } struct stu * insert (struct stu * head , struct stu *pi) { struct stu *pf , *pb; pb=head; if (head==NULL) /*空表插入*/ { head=pi; pi->next=NULL; } else { while((pi->num>pb->num)&&(pb->next!=NULL)) { pf = pb; pb = pb->next; } /*找插入位置*/
例从链表中删除一个结点 if(pb->num ==num) { if(pb==head) head=pb->next; /*如找到被删结点,且为第一结点,则使head指向第二个结点,否则使pf所指结点的指针指向下一结点*/ else pf->next = pb->next; free(pb); printf("The node is deleted\n"); } else printf("The node not been foud!\n"); end: return head; } struct stu * delete(struct stu * head ,int num) { struct stu *pf,*pb; if(head==NULL) /*如为空表, 输出提示信息*/ { printf("\nempty list!\n"); goto end; } pb =head; while (pb->num!=num && pb->next!=NULL) /*当不是要删除的结点,而且也不是最后一个结点时,继续循环*/ { pf = pb ; pb = pb->next; } /*pf指向当前结点,pb指向下一结点*/
D 例:以下程序运行后的输出结果是______。#include <stdlib> struct NODE {int num; struct NODE *next; } main() { struct NODE *p,*q,*r; p=(struct NODE *)malloc(sizeof(struct NODE)); q=(struct NODE *)malloc(sizeof(struct NODE)); r=(struct NODE *)malloc(sizeof(struct NODE)); p->num=10;q->num=20;r->num=30; p->next=q;q->next=r; printf("%d\n",p->num+q->next->num); } A) 10 B) 20 C) 30 D) 40
5 9 7 例:若以下定义: struct link { int data; struck link *next; }a,b,c,*p,*q; 且变量a和b之间已有如下图所示的链表结构:指针p指向变量a, q指向变量c。则能够把c插入到a和b 之间并形成新的链表的 语句组是____ a b data next data next ↑p c data next ↑q A) a.next=c; c.next=b; B) p.next=q; q.next=p.next; C) p->next=&c; q->next=p->next; D) (*p).next=q; (*q).next=&b; D
8 4 3 例:假定建立了以下链表结构,指针p、q分别指向如图所示 的结点,则以下可以将q所指结点从链表中删除并释放该 结点的语句组是 ______ A) free(q); p->next=q->next; B) (*p).next=(*q).next; free(q); C) q=(*q).next; (*p).next=q; free(q); D) q=q->next; p->next=q; p=p->next; free(p); B data next data next data next head ↑p ↑q
i ch f • 9.6 共用体 • 构造数据类型,也叫联合体 • 用途:使几个不同类型的变量共占一段内存(相互覆盖) • 共用体类型定义 定义形式: 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; ()
变量的各成员同时存在 struct node { char ch[2]; int k; }a; ch a k ch b k union node { char ch[2]; int k; }b; 任一时刻只有一个成员存在 • 结构体与共用体 • 区别: 存储方式不同 • 联系: 两者可相互嵌套
class name num sex job position Li 1011 F S 501 Wang 2086 T prof M 例 结构体中嵌套共用体 struct { char name[10]; int num; char sex; char job; union { int class; char position[10]; }category; }person[2];
9.7 枚举类型 1.枚举类型的定义 enum 枚举类型名 {取值表}; 例如,enum weekdays {Sun,Mon,Tue,Wed,Thu,Fri,Sat}; 2.枚举变量的定义──与结构变量类似 (1)间接定义 例如,enum weekdays workday; (2)直接定义 例如,enum [weekdays] {Sun,Mon,Tue,Wed,Thu,Fri,Sat } workday; 3.说明 (1)枚举型仅适应于取值有限的数据。 例如,根据现行的历法规定,1周7天,1年12个月。 (2)取值表中的值称为枚举元素,其含义由程序解释。 例如,不是因为写成“Sun”就自动代表“星期天”。事实上, 枚举元素用什么表示都可以。
(3)枚举元素作为常量是有值的──定义时的顺序号(从0开始),所以枚举元素可以进行比较,比较规则是:序号大者为大!(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。
例 枚举举例 # include " stdio.h" main( ) { enum weekday { sun , mon , tue , wed , thu , fri , sat } a ; scanf (" %d ", &a); /*以整型方式输入枚举变量*/ switch (a); { case mon: case tue: case wed: case thu: case fri: printf("工作日\n");break; case sun: case sat: printf (" 休息日\n ");break; default : printf(" 输入错误\n "); } }
9.8 用typedef定义类型 • 简单的名字代替: typedef int INTEGER; INTEGER a,b; • typedef可以定义新的类型名来代替已有的类型名. • 定义一个类型名代表一个结构体: typedef struct {……}STUDENT; STUDENT studend1,student2,*p;
练习题 1、设有如下定义: struct sk {int a; float b; }data; Int *p; 若要使p指向data中的成员a,正确的赋值语句是()。 A. p=&a; B.p=data.a; C.p=&data.a; D.*p=data; 2、以下各选项企图说明一种新的类型名,其中正确的是()。 A.typedef v1 int; B.typedef int v3; C.typedef v2=int; D.typedef v4:int;