首页计算机计算机软件C语言程序设计 09 第8章 结构及其地数据类型.pptx

09 第8章 结构及其地数据类型.pptx

页数:82页文件大小:367.7 KB来源: C语言程序设计ppt课件

本课件先介绍typedef与用户定义类型的写法,随后系统讲解结构体:类型与变量的定义、成员引用与初始化、结构体数组与结构体指针,以及链表的结构与基本操作;接着说明共同体的含义与存储特点,最后给出枚举类型与位运算、位段的用法。

第8章 结构及其地数据类型,讲解了解TYPEDEF的作用、理解结构体的含义、掌握结构体类型变量的定义方法等内容
教学内容1.结构类型,讲解说明、结构变量定义
第八章 引言我们已经掌握了利用数组来组织,讲解一组类型相同、还有很多实际问题的数据表示没有解决、一个管理学生信息的程序等内容
查看本课件的文字版(共 82 页,供检索与复制)

第1页:第8章 结构及其地数据类型

第8章 结构及其地数据类型

了解TYPEDEF的作用。

2.理解结构体的含义。

3.掌握结构体类型变量的定义方法。

4.掌握结构体类型变量的引用方法。

5.掌握结构体类型变量如何在定义的同时初始化。

6.理解共同体的含义,掌握共同体变量的定义方法。

7.了解结构体数组的定义和数组元素的引用。

8.了解指向结构体类型数据的指针的概念及使用。

9.了解链表结点的结构形式,链表的基本操作。

10.了解枚举类型的定义,及枚举类型的输入输出。

11.掌握各种位运算符,运算规则和优先级。

12.了解位运算的实际应用。

13.了解位段的定义和应用。

教学要求第8章结构及其地数据类型

第2页:教学内容1.结构类型

教学内容1.结构类型

说明、结构变量定义3.结构与函数4.结构指针与链表2.结构变量的初始化和使用1.位运算2.位段1.联合类型说明、联合变量定义2.联合变量的初始化和使用

第3页:第八章 引言我们已经掌握了利用数组来组织

第八章 引言我们已经掌握了利用数组来组织

一组类型相同,关系密切的数据,利用指针实现了对已知类型的对象的间接操作。

但还有很多实际问题的数据表示没有解决。

比如,一个管理学生信息的程序,关于学生的数据可能有姓名、性别、出生日期、住址、系别、专业、成绩等,它们不是相互独立的数据,逻辑上,应该把它们看作一个整体,因为它们共同描述了一个学生的情况。

这些信息的类型不同,无法用数组表示,需要另外的数据描述机制,结构与联合是C提供的另一类重要的构造类型,它们是编写复杂程序时常用的类型。

第4页:8.1 类型定义编程时自己定义的数据类型

8.1 类型定义编程时自己定义的数据类型

作用简化对数据的描述。

用新类型名来代表已存在的类型名,并未产生新的类型,原有的类型名依然有效。

新的类型名和原有的各种基本类名一样,方便地用于定义变量、函数的返回值和其他构造类型的元素。

在此句前已定义的类型新类型的名字

  • 称作“用户定义类型”。C语言提供类型自定义的机制。
  • 类型定义形式typedef类型名标识符

第5页:8.1 类型定义

8.1 类型定义

例1typedefintINTEGER;给int类型定义一个新的名字INTEGER,在此之后,可以用INTEGER来定义整型变量。

如:INTEGERi,j,a[10];

例3typedefdoubleVECT[4];定义一种具有4个元素的双精度数组类型VECT。程序中VECTv1,v2;定义两个4个元素的双精度数组。

  • 举例
  • 例2typedefchar*CHARP;新类型名CHARP是字符指针型,表示的旧类型是char*,故CHARPp;等价于char*p

第6页:8.1 类型定义

8.1 类型定义

typedef与define不同简单字符置换预编译时处理define为已有类型命名编译时处理typedef

  • 例4typedefintMAT[3][4];定义3
  • 4的矩阵MAT。MATm1,m2;定义两个3
  • 4的整数矩阵。intfun(MATm);说明了一个参数类型为MAT的整型函数fun。
  • 例5typedefchar(*funp)(int);(略)
  • 说明
  • typedef没有创造新数据类型
  • typedef是定义类型,不能定义变量

第7页:8.1 类型定义

8.1 类型定义

  • typedef定义类型步骤
  • 按定义变量方法先写出定义体;如:inti
  • 将变量名换成新类型名;如:intINTEGER
  • 最前面加typedef;如:typedefintINTEGER
  • 用新类型名定义变量。如:INTEGERi,j;例:定义数组类型
  • inta[50]
  • intARRAY[50]
  • typedefintARRAY[50]
  • ARRAYa, b
  • inta[50], b[50];例:定义指针类型
  • char*s
  • char*STR
  • typedefchar*STR
  • STRp,s[10]
  • char*p,*s[10]

第8页:8.2 结构结构可以将若干个不同类型的数

8.2 结构结构可以将若干个不同类型的数

据组合成一个复合数据对象。

组成结构的数据项称结构的成分或成员。

结构及其成员均给其一个名字,以实现对结构成员的访问。

有些资料中将结构称为结构体。

以后叙述中使用结构,既指类型,也指实体变量,视上下文的语义决定结构的含义。

对于某个具体的结构类型,成员的数量必须固定,但该结构中各个成员的类型可以不同。

如表示学生信息的结构,可以包括姓名、性别、生日和成绩四个成员,但这些成员的类型不同,姓名、性别是字符串,生日是数字,成绩是包含4个浮点数的数组。

第9页:8.2 结构

8.2 结构

学生信息的结构类型说明如下:structstudent{charname[15];charsex;structdatebirthday;floatscore[4]};

  • 类型说明、结构变量定义1.结构类型说明
  • 一般形式struct结构名{成员说明序列}
  • 实例
  • 日期可用包含三个成员的结构来描述:structdate{intyear,month,day;}

第10页:8.2 结构

8.2 结构

类型说明不分配内存!struct{ intyear, month, day;};struct{charname[15];charsex;structdatebirthday;floatscore[4];};datestudent

  • 解释
  • 成员说明序列包括各成员的类型及名字,与变量定义一样,可用一个类型名说明一个成员。也可同时说明几个成员
  • 成员名可与程序中的其他变量同名,不同结构中的成员也可同名
  • 可以用"struct结构名"的形式代表整个说明,相当于一个类型名。
  • 成员可以是已说明过的结构类型名。
  • 名是结构类型说明的标志,可省略

第11页:8.2 结构

8.2 结构

可以把“struct结构名”理解为一个结构类型名,和C语言中其他类型名的使用方法一样。

如:structdate是一个结构类型名;

structstudent也是一个结构类型名。

  • 注意
  • 类型定义可以嵌套(即结构成员可以是结构类型),ANSI允许嵌套至少为15层
  • 类型定义不允许递归(即结构成员是被描述的结构本身)
  • 类型与结构变量概念不同:变量类型操作分配不分分配内存可以不能赋值、存取、运算

第12页:8.2 结构

8.2 结构

方法一直接定义结构类型变量struct{成员说明序列}结构变量表;将一个结构说明当成类型描述,在它后面列出变量标识符,这种方法没有结构名。

例:struct{charname[10];intage;intclass;}stu1,stu2[5],*stu3;说明结构类型同时定义一个结构变量,一个具有5个元素的数组,一个基类型为该结构类型的指针变量。

  • 结构变量定义

第13页:8.2 结构

8.2 结构

方法二声明类型的同时,定义结构变量struct结构名{成员说明序列}变量表;例:struct{charname[10];intage;intclass;}stu1,stu2[5],*stu3;

方法三使用已声明的结构类型定义变量struct结构类型名结构变量表;例:用上例的structstudent定义结构变量structstudentp1,*p2;结构名student

第14页:8.2 结构

8.2 结构

方法四定义结构类型,再定义结构变量typedefstruct{成员说明序列}结构类型名;结构类型名结构变量表;

例:在关于图形的程序中,经常使用描述三维空间点的结构类型structpoint,可以考虑将其定义成一个新类型,使用新的类型名定义结构变量:typedefstruct{doublex,y,z;}POINT;POINTp1,p2[5],*p3;POINT是一个结构类型名,能唯一标识结构类型,可用它来定义变量,如同int、char一样,不必再写struct。

第15页:8.2 结构

8.2 结构

的实现存储结构对象,需要存储它的各个成员,C语言为结构对象分配足够大的存储空间,顺序地放入各个成员,但结构里的成员未必紧接着存放,之间可能有空位,由一组对齐规则来调整。

因此,整个结构存储单元的数量,不一定是各成员长度的简单累计。

其大小的应当用sizeof来计算。

引起结构存储问题的原因,是各成员的类型不同,占用空间大小不同,为了执行的效率,计算机对于基本类型的数据通常都有规定的摆放方式。

如对占两字节的整数,要求从偶数地址开始存放。

这些关于对象存放位置的要求,称作“对齐”。

第16页:8.2 结构

8.2 结构

变量的初始化和使用1.结构变量的初始化和一般变量、数组一样,结构变量和结构数组也可以在定义的同时初始化。

实现对结构提供初始值的表示形式与对数组提供初始值一样,使用{}将变量的成员的初始化值组合起来实现初始化。

举例structpoint{doublex, y ;}p1={1.5, 4.6},p[3]={{1.5, 2.5},{3.4, 2.4},{8.6, 9.0}};

第17页:8.2 结构

8.2 结构

给出的初始化数据与结构成员类型一致,个数不得多于成员数量,如果提供的数据项不够,与数组一样,其余成员自动用0初始化。

默认值:如果定义时没有提供初始值,系统对结构变量的处理方式与其他变量一样。外部和全局变量,用0初始化,自动变量不进行初始化,各成员的状态不确定。

  • 说明
  • 初始化描述中的初始值将顺序提供给结构变量的各基本成员。
  • 初始化表达式只能是可静态求值的表达式。

第18页:8.2 结构

8.2 结构

数组初始化时,结合结构和数组初始化的方法。

也可以像数组初始化一样,由初始化结构元素的个数决定数组的大小。

例:structbookcard{charnum[6];floatmoney;}bk[]={{"NO.1",55.6},{"NO.2",90.5},{"NO.3",22.5};}

第19页:8.2 结构

8.2 结构

整体赋值同样类型的结构变量,可整体进行相互赋值,效果是相应成员分别进行赋值。

例如:s1和s2是两个结构变量,它们的类型相同,则可用s2=s1将s1结构的各成员赋值给s2的相应成员。

  • 结构变量操作

第20页:8.2 结构

8.2 结构

成员引用的两个运算符"."和"->"点号"."称成员运算符,箭头"->"称结构指向运算符,这些运算符在C语言的运算符中优先级最高,自左向右结合。

  • 结构成员引用
  • 中简单变量成员的引用三种形式
  • 名字引用:结构变量名.成员名
  • 指针引用:结构指针->成员名
  • 指针转为名字引用:(*结构指针).成员名
  • 中构造类型成员引用用连续的"."和"->"从外到内逐层引用。

第21页:8.2 结构

8.2 结构

若要引用stu1成员sex,可写成:stu1.sex或pstu->sex或(*pstu).sex

若要引用stu1中数组成员score[2]时,可写成:stu1.score[2]或pstu->score[2]或(*pstu).score[2]

若要引用数组stu2中的第3个成员中的sex,可写成:stu2[2].sexstructstudent{charname[15];charsex;structdatebirthday;floatscore[4]}stu1,stu2[5],*pstu;pstu=&stu1

  • 实例

第22页:8.2 结构

8.2 结构

若结构中成员是作为串使用的字符型数组,如name,可写成:stu1.name或pstu->name或(*pstu).name

内嵌结构成员的引用如stu1的年份可写成:stu1.birthdny.year或pstu->birthday.year或(*pstu).birthday.year

C语言不允许对数组整体访问,只能逐个引用其元素。

structstudent{charname[15];charsex;structdatebirthday;floatscore[4]}stu1,stu2[5],*pstu;pstu=&stu1

第23页:8.2 结构

8.2 结构

对结构成员进行操作结构变量中的每一个成员都属于某个具体的类型,可以像普通变量一样,对它进行同类变量所允许的任何操作。如下例:

scanf("%s",stu1.name); strcop(stu1.name,"Zhangyang); scanf("%d",&stu1.birthday.year);scanf("%c",&stu1.sex); stu1.sex='M'stu1.birthday.year=1963structstudent{ charname[15]; charsex; structdatebirthday; floatscore[4] } stu1;

第24页:8.2 结构

8.2 结构

与函数1.向函数传递结构变量的成员C函数的实参、形参传递数据只有值传递,结构变量的成员可以是简单变量、数组或指针变量等,作为成员变量,可以参与其类型允许的任何操作,这个原则也适用于参数传递。

2.向函数传递结构变量把结构变量作为一个整体传递给函数,要求形参也属于相同的类型。

这时系统传递的是实参结构变量的值,系统将为形参开辟相应的存储单元,并将实参中各成员的值赋给对应的形参成员。

第25页:8.2 结构

8.2 结构

函数mkcard(int,char)由参数构造一个CARD结构,并返回CARD类型结构值。

  • 例7用给定参数,构造一个结构变量的值。
  • 分析
  • 本例的结构是描述扑克牌的结构,由于程序中要多处使用,所以将其定义为结构类型CARD。
  • 函数pcard(CARD)打印CARD结构信息。
  • 程序*运行结果:('S', 5)

第26页:8.2 结构

8.2 结构

typedefstruct{intpips;//牌点charsuit;//字体做调整}CARD;CARDmkcard(int,char);voidpcard(CARD);

main(){ CARDmycard; mycard=mkcard(5,'S'); pcard(mycard); } CARD mkcard(pips, suit)intpips; charsuit; { CARD temp; temp.pips= pips; temp.suit= suit; return( temp ); } voidpcard( CARD c ){ printf("(\'%c\',%d)\n",c.suit,c.pips); } suitpipsS5suitpipsS5

第27页:8.2 结构

8.2 结构

问题首先,结构变量包含较多的成员,系统要为相应的形参开辟一片存储区,一一传递各成员的数据;

另一方面,函数参数值传递的方法,限制函数将结果返回给调用函数,使函数返回计算结果的问题变得复杂,为了返回后取用函数计算的结果,函数或者返回结构变量,或为保存结构参数中成员的当前值进行一系列内部操作。

所有这些势必增加系统的处理时间,影响程序的执行效率。

第28页:8.2 结构

8.2 结构

优点系统只需为形参开辟一个存储单元存放实参结构的地址,不必另行建立一个结构变量,这样可以有效地减少系统开销,最重要的是通过指针传递,可直接在函数中有效地操作结构成员。

这是指针作函数参数的共性。

  • 传递结构的地址
  • 参数传递方法采用结构指针做函数实参,对应形参应该是一个基类型相同的结构指针。

第29页:8.2 结构

8.2 结构

分析本例定义一个简单的学生信息结构student,通过将结构地址传给函数,修改学生的年龄。对于指针形参,相应的实参可以是基类型相同的地址和指针变量。

  • 例8结构指针作为函数参数
  • 程序*

第30页:#include

#include

<stdio.h>typedefstruct{charname[15];intage;}student;voidaddage(student*s){s->age++;}

main(){ student*stu1,stu2={ "ZhangYang",20 } ; stu1=&stu2; addage(stu1); printf("%s%d\n",stu1->name,(*stu1).age); addage(&stu2); printf("%s%d\n",stu2.name,stu2.age); } 8.2结构运行结果:Zhang Yang 21Zhang Yang 22agenamestu220Zhang Yangstu1→

第31页:8.2 结构

8.2 结构

链表特点使用结构指针和动态存储管理机制,可以实现一种叫链表的机制。

它的特点是将数据存储在位置任意的结构存储块中,用结构指针将这些存储块连接在一起,从第一个存储块顺着指针可以访问到所有的数据,如图所示。

99…23325124P

  • NULL链表结构示意图

第32页:8.2 结构

8.2 结构

链表有关概念链表中的每一个存储块叫一个结点。指向第一个结点的指针叫头指针。关于链表结构的使用,后续课程将会有更深入的讨论。

  • 链表的实现实现链表的基础是表示结点的结构类型中有一个指向本结构类型的指针成员。
  • 举例
  • 例9*构造下图具有三个结点的简单链表过本例,了解链表的基本概念及简单操作。程序如下:

第33页:8.2 结构

8.2 结构

#include<stdio.h>structnode{ intdata; structnode*next; } ; typedefstructnodeNTYPE; main(){ NTYPEa,b,c,*h,*p; a.data=10; b.data=20; c.data=30; a.next=&b; b.next=&c; c.next=NULL; h=&a; p=h; while(p){ printf("%4d",p->data); p=p->next; } } 10a20b30∧ch运行结果:10 20 30

第34页:8.2 结构

8.2 结构

分析本例演示动态构造链表的方法,程序从键盘接收数据,通过动态存储空间管理的函数申请结点空间,构造结点和链表,读到负值表示结束,最后打印当前链表中结点的数据和个数。

为了操作方便,第一个结点不放数据,叫链表的头结点。

  • 例10动态构造链表。
  • 程序*

第35页:8.2 结构

8.2 结构

#include<stdlib.h>main(){ NTYPE*h,*p,*q; intdata; h=(NTYPE*)malloc(sizeof(NTYPE)); if(h==NULL)exit(0); h->next=NULL; q=h; printf("输入数据:"); 运行结果:输入数据:10 20 30 40-1
scanf("%d", &data); if(data<0) break; p=(NTYPE *) malloc(sizeof(NTYPE)); if(p==NULL) exit(0); p->data=data; p->next=NULL; q->next=p; q=q->next; } p = h->next; while(p){ printf("%5d", p->data); p=p->next; } }
  • structnode{intdata;structnode*next;};typedefstructnodeNTYPE
  • 10 20 30 40while(1){

第36页:8.2 结构

8.2 结构

例11分析下面一个函数的问题CARD是例7中定义的结构类型。

CARD*mkcard(intpips,charsuit){CARDtemp;

temp.pips=pips;

temp.suit=suit;

return&temp;

}函数中定义了一个临时结构变量temp,返回这个结构的地址,但作为临时变量,函数返回后temp的存在期即告结束,因此,无法再使用返回指针值所指的空间。

这是一个非常严重的错误,使用中要避免。

第37页:8.2 结构

8.2 结构

动态链表操作链表是最简单、最重要的结构形式之一,是最经常遇到的一种操作对象,应用相当广泛。下面将介绍链表创建、输出、查找、插入和删除等运算。

#include<stdlib.h>#include<stdio.h>structnode{ intdata;} ; typedefstructnodeNTYPE;
  • 必要的头文件与链表节点类型定义*

第38页:8.2 结构

8.2 结构

动态链表构造函数*NTYPE*creat_link(){NTYPE*h,*p,*q;intdata;if((h=(NTYPE*)malloc(sizeof(NTYPE)))==NULL)exit(0);h->next=NULL;q=h;

printf("\nEnterdata:"); scanf("%d",&data); if(data<0)break; p=(NTYPE*)malloc(sizeof(NTYPE)); if(p==NULL)exit(0); p->data=data; p->next=NULL; q->next=p; q=q->next; } p=h; h=h->next; free(p); returnh; }

第39页:8.2 结构

8.2 结构

动态链表输出函数*voidprn_link(NTYPE*hd){NTYPE*p=hd;while(p){

printf("%d",p->data); p=p->next; } printf("\n"); }
main(){ NTYPE*head; head=creat_link(); prn_link(head); } 5040302010head∧链表结构示意图运行结果:Enter data:10 20 30 40 50-1
  • 链表创建主程序*
  • 10 20 30 40 50

第40页:8.2 结构

8.2 结构

找到所在结点的指针在已建的链表中,查找值x,若找到则返回所在结点的指针,否则返回空指针。

NTYPE*search1(hd,x)NTYPE*hd;intx;{NTYPE*p=hd;while(p&&p->data!=x)p=p->next;returnp;}40302010head∧链表结构示意图

main ( ){ NTYPE*head,*p; head=creat_link( ); p=search1(head,30); if(p!=NULL)printf("%d",p->data); elseprintf("Not found"); } 运行结果:Enter data:10 20 30 40-1
  • 链表的查询*
  • 30

第41页:8.2 结构

8.2 结构

找到前一个结点的指针在链表中,查找值x,若找到则返回x结点前一个结点的指针,否则返回最后一个结点指针。

NTYPE*search2(hd,x)NTYPE*hd;intx;{NTYPE*p=hd;if(p)while(p->next!=NULL&&p->next->data!=x)p=p->next;returnp;}40302010head∧链表结构示意图

main ( ){ NTYPE *head,*p; head=creat_link( ); p=search2(head,30); printf("%5d",p->data); p=search2(head,15); printf("%5d",p->data); } 运行结果:Enter data:10 20 30 40-1
  • 20 40

第42页:链表的插入*

链表的插入*

在结点x后面插入avoidinsert(hd,x,a)NTYPE*hd;intx,a;{NTYPE*p,*q;if(q=search1(hd,x)){p=(NTYPE*)malloc(sizeof(NTYPE));if(p==NULL)exit(0);p->data=a;p->next=q->next;//

printf("Notfount"); } main ( ){ NTYPE *head; head=creat_link( ); insert(head,30,35); prn_link( head); } 8.2结构5040302010head∧q运行结果:Enter data:10 20 30 40 50-1
  • q->next=p;//
  • 10 20 303540 50p
  • 35
  • hd

第43页:8.2 结构

8.2 结构

在表头插入aNTYPE*insert_h(hd,a)NTYPE*hd;inta;{NTYPE*p;p=(NTYPE*)malloc(sizeof(NTYPE));if(p==NULL)exit(0);p->data=a;p->next=NULL;if(hd!=NULL)p->next=hd;//

main ( ){ NTYPE *head; head=creat_link( ); head=insert_h(head,5); prn_link(head); } 运行结果:Enter data:10 20 30 40-1
  • hd=p;returnhd;//
  • }40302010∧head
  • 510 20 30 40p5hd

第44页:8.2 结构

8.2 结构

在表尾插入aNTYPE*insert_t(hd,a)NTYPE*hd;inta;{NTYPE*p,*q=hd;p=(NTYPE*)malloc(sizeof(NTYPE));if(p==NULL)exit(0);p->data=a;p->next=NULL;if(q!=NULL){while(q->next)q=q->next;//将q移到表尾q->next=p;}elsehd=p;//插入到空表returnhd;}//以上各情况可合并成一函数

main ( ){ NTYPE *head; head=creat_link( ); head=insert_t(head,35); prn_link(head); } 运行结果:Enter data:10 20 30-1
  • 10 20 3035302010headhdqp35∧∧

第45页:8.2 结构

8.2 结构

链表的删除*NTYPE*del(NTYPE*hd,intx){NTYPE*q,*p=hd;if(hd==NULL)returnNULL;if(hd->data==x)hd=hd->next;//删除头结点else//q移到x前一结点{q=search2(hd,x);p=q->next;if(p)q->next=p->next;}free(p);returnhd;}7531hd∧qp731∧hdp运行结果:Enter data: 1 3 5 7-1

main ( ){ NTYPE *head; head=creat_link( ); head=del(head,5); prn_link (head); head=del(head,1); prn_link (head); } p
  • 1 3 73 7

第46页:8.3 联合联合

8.3 联合联合

也是C语言的一种构造机制,有的资料中称其为共同体或共用体。

一个联合通常由几个类型不同的成员组成,这些成员共享同一个位置开始的存储区。

也就是说,每一个时刻,联合只能保持它的某一个成员的值。

从这个意义上讲,叫公用体似乎更确切。

第47页:8.3 联合

8.3 联合

说明和变量定义的形式与结构相同,不同的只是关键字(union)。

uniondata{shorti;floatf;charstr[5];}d1,d2联合data同时定义了两个联合变量d1和d2。

ifstr[5]

  • 类型说明、联合变量定义
  • 类型说明形式union{成员说明}变量表;union联合名{成员说明}变量表
  • 举例

第48页:8.3 联合

8.3 联合

可以出现在结构内,其成员也可以是结构。

struct{charname[10];union{introomnum;char*addr;}info;floatf;}x[5];finfonamex:70.2201zhao060.5302qian150.1sun265.1li340.8102zhou4chinaxiamen

第49页:8.3 联合

8.3 联合

变量的初始化和使用1.联合变量初始化联合变量定义时也可以初始化,但初始化值只能给第一个成员。如:uniondatad1=10;2.联合变量的使用

  • 联合成员引用
  • 成员引用方法,与结构成员引用方法完全相同,共三种形式:
  • 变量名.成员名
  • 指针变量名->成员名
  • (*指针变量名).成员名

第50页:8.3 联合

8.3 联合

printf("%f\n",d1.f); 最近一次存储是d1.i=100,输出语句的输出项是d1.f,系统不报告错误,将按f
  • 说明
  • 的成员变量同样可参与所属类型允许的所有操作。
  • 成员的赋值操作将覆盖原空间的数据
  • 成员的访问操作取到的值总是最近一次存入的成员变量的值。
  • 如:d1.f=123.5;d1.i=100

第51页:8.3 联合

8.3 联合

若要访问x[2]中联合info的串指针addr的第一个字符,可写成:*x[2].info.addr;

finfonamex:01.2302zhang13.4li234china

  • 例如:struct{charname[10];union{introomnum;char*addr;}info;floatf;}x[5]
  • 若要访问x[1]中联合info的成员roomnum,可以写成:x[1].info.roomnum

第52页:8.3 联合

8.3 联合

变量整体赋值新ANSIC标准允许在两个类型相同的联合变量之间进行赋值操作。赋值结果是两个存储区内容相同。

和函数有关的联合和结构一样,不仅联合成员可以像简单变量一样参与函数操作,联合变量和联合指针可以作为函数形参、返回值并可以调用实参。

第53页:8.3 联合

8.3 联合

变量存储区的大小由联合成员中要求最大存储空间的成员的空间要求决定。理解联合变量操作的基础,仍然是数据在计算机内部表示方面的知识。

对联合变量使用的原则保持使用和最近一次赋值的一致,即程序中,原来在联合空间中以什么方式存入数据,就按照该方式的要求使用存入的数据。

这种一致性靠编程者来实现,C语言不检查,实际上也无法进行严格检查,语言本身没有规定联合变量取值方式与前面赋值方式不一致应如何处理。

  • 变量的存储

第54页:12*联合体内成员共用同一块空间

12*联合体内成员共用同一块空间

main(){ union{ shorti; struct{ charfirst; charsecond; } half; } num; num.i=0x4241; num.half.first='a'; num.half.second='b'; printf("%x\n",num.i); } i:低高half:fistsecond4241相当于8.3联合4142ABi:低高half:fistsecondab相当于ab6162运行结果:AB6261

第55页:8.4 枚举枚举是C语言中用于定义一组命

8.4 枚举枚举是C语言中用于定义一组命

名常量的机制。如果一个变量只有可能的几种值,可以定义为枚举类型。枚举是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围。

第56页:8.4 枚举

8.4 枚举

  • 一般形式enum枚举类型名{枚举常量表}
  • 举例enumcolor{RED,GREEN,YELLOW,BLUE=5,WHITE,BLACK}
  • 说明
  • 型仅适应于取值有限的数据;例如,根据现行的历法规定,1周7天,1年12个月。
  • 常量标识符不能再做其他量的名字。

第57页:8.4 枚举

8.4 枚举

常量表中枚举常量(枚举元素),是用户定义的标识符,其含义由程序解释;例如,不是因为写成"RED"就自动代表"红色"。事实上,枚举元素用什么表示都可以。

可以用赋值号改变枚举常量的值,随后的枚举常量的值顺序递增,直到下一个指定特定值的枚举常量为止;

例如,RED=0、GREEN=1、YELLOW=3、BLUE=5、WHITE=6、BLACK=7,所以GREEN>RED、BLACK最大。

  • 常量的值(序号)从0开始,从左向右递增

第58页:8.4 枚举

8.4 枚举

一般形式enum枚举类型名枚举变量1,枚举变量2…;也可以在说明枚举类型的同时定义枚举变量。

  • 举例
  • 可用前面说明过的枚举类型来定义枚举变量:enumcolorbackcolor
  • 或直接定义枚举变量:enumcolor{RED,GREEN,YELLOW,BLUE,WHITE,BLACK}backcolor

第59页:8.4 枚举

8.4 枚举

C语言将枚举变量解释为整型数。引入枚举可以提高程序的可读性;且借助枚举类型可以对枚举变量的值进行约束,便于检查错误。

不能直接将一个整数赋值给枚举变量,它们的类型不同,应先进行强制类型转换再赋值。

如:backcolor=(enumcolor)2;

相当于把枚举元素YELLOW赋给backcolo。

  • 说明
  • 变量(如backcolor)的值只能是枚举类型中说明的枚举常量的值。
  • 可以使用枚举常量给枚举变量赋值。如:backcolor=RED

第60页:8.4 枚举

8.4 枚举

设fcolor是另一个与backcolor同类型的枚举变量。

如下的判断和比较经常使用:if(backcolor==RED)…if(backcolor>YELLOW)…if(backcolor!=fcolor)…

例13*输出枚举类型enumcolor中,每个枚举常量代表的颜色名称。分析:枚举类型中的枚举常量不能作为字符串输出,本例使用switch结构实现。

  • 除了赋值,枚举变量还可进行判断和比较。

第61页:8.4 枚举

8.4 枚举

#include<stdio.h>enumcolor{ RED,GREEN=4,YELLOW,BLACK } ; main(){ enumcoloc; for(c=RED; c<=BLACK; c++){ switch(c){ caseRED:printf("%s,","red"); break; caseGREEN:printf("%s,","green"); break; caseYELLOW: printf("%s,","yellow"); break; caseBLACK:printf("%s\n","black"); break; } } } 运行结果:red, green, yellow, black

第62页:8.5 位运算和位段转下一部分实际问题

8.5 位运算和位段转下一部分实际问题

中,有一些数据的变化情况比较简单,只需要用一个或几个二进制位就能够表示。

比如,很多表示开关状态的数据,只有0和1两种值,用一个二进制位就可以表示。

许多系统程序、硬件的控制程序需要对二进制位表示的数据直接操作,C语言的主要设计目的是面向复杂的系统程序设计,特别提供了对二进制位的操作功能。

用位运算符实现二进制直接操作。

第63页:8.5 位运算和位段

8.5 位运算和位段

位运算一个位只能取值0或者1,位运算从一个或两个具有0或1的运算对象出发,计算出具有0或1的结果。

位运算对象位运算的实质是对字节或字中的实际位进行检测、设置或移位,它只适用于字符型和整型数据以及它们的变体,对其他类型不适用。

位运算方法根据位运算定义了一组位运算符,计算时,把运算对象看作二进制位的序列,对于每个位分别进行位运算,由此得到结果的各个位。

第64页:8.5 位运算和位段

8.5 位运算和位段

  • 常用的位运算及运算规则
  • 位“否定”一个运算对象,对象值为1时得到0值,对象值为0时得到1值。
  • 位“与”两个运算对象的值都为1时得到结果1,其他情况结果为0。
  • 位“或”两个运算对象的值都为0时得到结果0,其他情况结果为1。

第65页:8.5 位运算和位段

8.5 位运算和位段

右移一个运算对象,将变量中的每一位向右移动指定位数。右端的位被“挤掉”,而左端空出的位以0填补。

  • 位“异或”两个运算对象的值不相同时得到结果1,否则结果为0。
  • 右移一个运算对象,将变量中的每一位向左移动。右端空出的位以0填补。

第66页:8.5 位运算和位段

8.5 位运算和位段

注意区别位运算符和逻辑运算符的不同如,若x=7,则x&&8的值为真,而x&8的值为0。按位或按位异或按位与右移左移按位求反名称|^&>><<~符号

  • 位运算符及优先级
  • 扩展运算符<<=、>>=、&=、^=和|=
  • 移位运算符语法形式右移:变量名>>移位的位数左移:变更名<<移位的位数
  • 优先级

第67页:8.5 位运算和位段

8.5 位运算和位段

左端移出部分舍弃,右端补0。

如:char a=4, b;b=a<<2;用二进制数表示:a:00000100(a=4)b=a<<2:00010000 (b=6=4×2×2)

左移时,若左端移出部分没有1,则每左移1位,相当于乘2。

可用此法代替乘法,加快运算速度。

若移出部分含1,这一特性不成立。

如:chara=68,b;b=a<<2;用二进制数表示:a:01000100(a=68)b=a<<2:00010000(b=16)

  • 例14位运算举例
  • “左移”运算(<<)

第68页:8.5 位运算和位段

8.5 位运算和位段

右端移出部分舍弃,左端移入二进制数保持原来数的性质,如果原来是正数或无符号数,左端移入补0,否则左端移入补1。

(这种移位叫算术移位,不同的C语言系统的处理方法可能不同,移入位全补0的移位操作则逻辑移位。

TC2.0采用算术移位)

  • “右移”运算

第69页:8.5 位运算和位段如

8.5 位运算和位段如

inta=-071000,b;b=a>>2;用二进制数表示的过程如下:a的原码:1111001000000000(a=-071000)a的补码:1000111000000000(机内形式)b=a>>2:1110001110000000(b机内补码)b的原码:1001110010000000b的八进制数:-016200

  • 若右端移出部分没有1,则每右移一位,相当于移位对象除以2。

第70页:8.5 位运算和位段

8.5 位运算和位段

按位“取反”(~)~0110将八进制数110按位取反,结果为:0267 (八进制)~0100100010110111

按位“与”(&)和0与的位得0,和1与的位保持不变。

可用和0与的方法,将某一位清0,可用和1与的方法取某一位的值。

如:12&10的计算过程如下:00001100&0000101000001000

第71页:8.5 位运算和位段

8.5 位运算和位段

按位“或”(|)和1或的位得1,和0或的位保持不变。

可用和1或的方法,将某一位置1,可用和0或的方法取某一位的值。

如:12|10的计算过程如下:00001100|0000101000001110

第72页:8.5 位运算和位段

8.5 位运算和位段

按位“异或”(∧)运算位上数相同得0,数不同得1。

可用和二异或的方法,对某一位求反,可用和0异或的方法保持某一位不变。

如:12^10的计算过程如下:00001100^0000101000000110

位运算对象长度不同时的处理将右端对齐,左端根据数的性质决定补0或1以保持数据值的性质不变。即int型的-5,扩展成long型后,值还是-5,再进行位运算。

第73页:8.5 位运算和位段

8.5 位运算和位段

例15设网络号占用IP地址的前3个字节,设计能判断某个IP地址是否属于该网络方法。IP地址,由4个字节组成,各字节中间加点。如:202

121,其中有一部分位段表示这台计算机所在网络的网络号,是网络寻址的根据。

实际解决方法是为每一个网络规定一个掩码,对应于网络地址的位设为1,其他的位设为0。

如果上述网络的掩码为255

  • 192
  • 195
  • 255
  • 255
  • 0,将掩码和IP地址进行按位与运算,即可求出所需的网络号。如:202
  • 192
  • 195
  • 121&255
  • 255
  • 255
  • 192
  • 195
  • 0

第74页:8.5 位运算和位段

8.5 位运算和位段

位段位段是一种特殊的结构成员,在需按位访问一个字节或字的多个位时,位段比按位运算更加方便。

它允许在结构中以位为单位来指定成员所占用的内存长度,这种以位为单位的成员也称“位域”。

位段定义的一般形式struct结构名{数据类型变量名:整型常数;

数据类型变量名:整型常数;

……}必须是unsigned是选择项,可以是位段成员的名称,也可以不命名,表示该位置空出若干位,这样规定是为了排列需要。

必须是非负整数,范围是0~15,表示二进制位的个数,即表示有多少位。

长度为0的位段表示下一个位段从下一个存储单元开始存放。

第75页:8.5 位运算和位段

8.5 位运算和位段

位段的存储一个位段必须存储在同一存储单元中,不能跨两个单元。

如果第一个单元空间中不能容纳一个段位,则该空间不用,而从下一个单元开始存放。

这样在存储空间中,位段的存储之间可能有空的位没有使用。

位段的输出与引用位段可以用整型格式输出,也可在数值表达式中引用,系统自动将其转换成整型数。位段成员的引用与结构成员的访问相同。

  • 说明

第76页:8.5 位运算和位段

8.5 位运算和位段

例如下面定义了一个包含4个位段成员的结构变量。

struct{unsignedincon:8;//占用低字节的0~7位unsignedtxcolor:4;//占用高字节的0~3位unsignedbgcolo:3;//占用高字节的4~6位unsignedblink:1;//占用高字节的第7位}ch;

访问结构中的bgcolor成员可写成:h.bgcolorincontxcolorbgcoloblink0123456701234567

第77页:8.5 位运算和位段

8.5 位运算和位段

位段成员可以与其他结构成员一起使用。

例如:structinfo{charname[8];intage;structaddraddress;floatpay;unsignedstate:1;unsignedpay:1;}workers;例子中定义了关于一个工人的信息。

其中有两个位段成员,每个位段成员只有一位,因此最后一个字节内保存了两个信息。

该字节中第一位表示工人的状态,第二位表示工资是否已发放。

由此可见,使用位段可以节省存储空间。

  • 注意
  • 位段成员不能使用数组和指针。

第78页:8.5 位运算和位段

8.5 位运算和位段

例16将1023只苹果分放在10个箱子中,怎样分可保证想要任何数量的苹果都可整箱分配。

分析本例对计算机系的学生来说,有特别的意义,因为解决方案的灵感来自二进制数的表示方法。

将箱子编号0、l、2、…、9,在0号箱子里放1只苹果,在1号箱子里放2只苹果,在2号箱子里放22只苹果,以此类推,在9号箱子里放29只苹果。

对任意不超过1023的数,可根据其内部表示第i位是否为0决定是否将第i个箱子进行分配。

第79页:8.5 位运算和位段

8.5 位运算和位段

main(){ unsignedi,k,num,apple[10]; printf("EnterNumber(<1024):"); scanf("%d",&num); for(i=1,k=0; k<10; k++)//计算各箱内应放苹果数{ apple[k]=i; i<<=1; } printf("No.Numbr:\n"); i<1024; k++){ if(i&num)printf("%3d%4d\n",k,apple[k]); i<<=1; } } 运行结果:Enter Number(<1024):465
  • No.Numbe:0141666471288256

第80页:8.6 小结本章主要包括以下内容

8.6 小结本章主要包括以下内容

结构的基本概念。

包括结构与成员的基本概念,结构与数组的区别,结构类型的说明,变量的定义,结构中成员占用存储器的情况,与结构有关的运算符,引用结构中的成员,结构变量的初始化,对结构成员操作的一般规定。

  • 结构与函数的关系。
  • 结构与数组关系。

第81页:8.6 小结

8.6 小结

结构与指针的关系。

包括指向结构的指针与结构指针相关的运算符、通过指针引用结构成员、结构的地址与结构成员的地址、运算符->与其他相关运算符(++,--,&,*和

  • )的关系和如何在函数之间传递结构指针。
  • 结构的简单应用。包括链表的概念、用结构构成的链表和链表的基本操作。

第82页:8.6 小结

8.6 小结

包括联合与结构区别、联合类型的说明、联合变量的定义、联合中成员占用存储器情况、与联合有关的运算符、如何引用联合中的成员和联合变量的初始化。

  • 联合的基本概念。
  • 位运算和位段。
  • 类型定义。
  • 枚举。
顶部目录