550 likes | 629 Views
第七章 结构与联合. 7.1 结构体的定义 7.2 结构体变量的定义 7.3 结构体成员的访问 7.4 结构数组与程序举例 7.5 结构与函数 7.6 结构与链表 7.7 结构与操作符重载 7.8 联合. 7.1 结构体的定义及引用. 问题提出. 有时候需要 将不同类型的数据组合成一个有机的整体 ,这些组合在一个整体中的数据是互相联系的。. 如一个学生的信息包括学号 、 姓名、性别、年龄、成绩、地址等。. int num;. 应当把它们组织成一个组合项,在一个组合项内包含若干个类型不同(或相同)的数据项。.
E N D
第七章结构与联合 7.1 结构体的定义 7.2 结构体变量的定义 7.3 结构体成员的访问 7.4 结构数组与程序举例 7.5 结构与函数 7.6 结构与链表 7.7 结构与操作符重载 7.8 联合
7.1 结构体的定义及引用 问题提出 有时候需要将不同类型的数据组合成一个有机的整体,这些组合在一个整体中的数据是互相联系的。 如一个学生的信息包括学号、姓名、性别、年龄、成绩、地址等。 int num; 应当把它们组织成一个组合项,在一个组合项内包含若干个类型不同(或相同)的数据项。 char name[20]; char sex; C语言提供了这种数据结构:允许用户将不同类型的数据组合成一个有机的整体,这些数据互相联系;这种数据结构称为结构体(structure)。 int age; float score; char addr[30];
7.1.1 结构定义的格式 所谓结构体的定义,是指定义相应的数据结构及相应的变量。 步骤:①、先定义一个结构体类型标识符; ②、用该标识符去定义相应的变量。 structstudent { int num; char name[20]; char sex; int age; float score; char addr[30]; } 说明: ①、struct是关键字,不能省略:表示将要进行结构体的定义; ②、student是用户自己定义的结构体类型标识符,即该结构体名; ③、结构体中具体每项的定义,由{ 和 }括起来。C语言中把用户自己定义的数据项称为成员(或称为域),即每个成员也叫结构体中的一个域。 ④ 最后以“;”结束结构的定义。 ;
结构体名 定义一个结构体类型的一般形式: struct结构体类型标识符 { 类型标识符1 成员名1; 类型标识符2 成员名2; …… …… 类型标识符n 成员名n; } 说明: ①、“结构体名”用作结构体类型的标志; 成员表列 ②、大括弧内是该结构体中的各个成员,由它们组成一个结构体;在结构体内对各成员都应进行类型声明; ; ③、“成员表列”也称为域表。每个成员也称为结构体中的一个域,成员名命名规则与变量名一样; ④、每个成员名前的类型标识符可以为已经定义了的任意类型,当然可以是结构体类型标识符,即结构体成员也可以是另一个结构体变量。 ⑤、此处只是构造出一个新的类型,并没有定义该类型的变量,因此在内存中并没有开辟任何存储空间; ⑥、在程序中可以定义多个结构体类型,不同结构体类型用不同的结构体名来区分。
7.2 结构体变量的定义和初始化 1、定义形式: [struct] 结构体类型标识符变量名列表; 注意:struct可省略。 2、定义结构体变量的三种方法: ①、先定义结构体类型再定义变量名 structstudent { int num; char name[20]; float score; }; struct student stu1,stu2; 结构体类型名 结构体变量名 stu1 stu2 在定义了结构体变量后,系统会为之分配内存单元。 sizeof(stu1)= 2+ 20+ 4 =26
②、在定义结构体类型的同时定义变量 struct结构体类型标识符 { 类型标识符1 成员名1; 类型标识符2 成员名2; …… …… 类型标识符n 成员名n; }变量名列表; structstudent { int num; char name[20]; float score; }stu1, stu2; ③、直接定义结构体类型变量 (不出现结构体类型名) struct { 类型标识符1 成员名1; 类型标识符2 成员名2; …… …… 类型标识符n 成员名n; }变量名列表; struct { int num; char name[20]; float score; }stu1, stu2;
4、关于结构体类型的几点说明: ①、类型与变量是不同的概念,不要混淆: a、只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算; b、在编译时,对类型不分配内存空间,只对变量分配空间。 ②、只有在定义变量后,才为该变量开辟存储单元。结构体变量所占的内存长度等于每个成员长度之和; structstudent { int num; char name[20]; float score; }; sizeof(struct student)=26 ③、对结构体中的成员(“域”),可以单独使用,它的作用与地位相当于普通变量;
④、成员也可以是一个已定义的结构体类型变量:④、成员也可以是一个已定义的结构体类型变量: struct date { int year; int month; int day; }; structstudent { int num; char name[20]; char sex; struct date birthday; char addr[30]; }; sizeof(struct student)=? ⑤、成员也可以是指向本结构体类型的指针变量; struct student { int num; char name[20]; float score; struct student*next; }; ⑥、成员名可以与程序中的变量名相同,二者不代表同一对象。
7.3 结构体成员的访问 结构体变量名.成员名 1、直接访问: “.”是成员运算符,在所有的运算符中优先级最高。 stu1.num=10001; structstudent { int num; char name[20]; float score; }stu1, stu2; stu1.score=95; stu1.name ="Li Ming"; strcpy(stu1.name, "Li Ming"); 2、间接访问 结构体变量的指针:就是该变量所占据的内存段的起始地址。struct studentstu, *p;p=&stu; 对结构体成员的访问有以下三种形式: ①、stu.num、stu.name、stu.score “->”为指向运算符,是优先级最高的运算符; ②、(*p).num、(*p).name、(*p).score ③、p->num、p->name、p->score Eg901.cpp
7.3 结构体变量的访问 3、只有在对结构体变量赋值或作为函数参数时才可以对一个结构体变量进行整体操作;(赋值时要求具有相同结构) stu2.num=stu1.num; strcpy(stu2,name, stu1.name); stu2.score=stu1.score; stu2=stu1; void fun(struct studentpx); fun(stu1); 注1、不能将一个结构体变量作为一个整体进行输入输出: cout<< stu1); cin>>stu1); 只能对结构体中的各个成员分别进行输入输出。 cout<< stu1.num<<stu1.name<<stu1.score); gets(stu1.name); cin>>stu1.num); Eg901.cpp
成员名也是一个变量(称为成员变量),具有自己的数据类型,使用与同类型的变量相同。 成员名也是一个变量(称为成员变量),具有自己的数据类型,使用与同类型的变量相同。 注2、如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级的找到最低一级的成员。只能对最低一级的成员进行赋值、存取或运算; stu1.birthday.year=2002; 注3、对成员变量可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算) sum=stu1.score+stu2.score; stu1.age++; 注4、可以引用结构体变量成员的地址,也可以引用结构体变量的地址; student *p= &stu); int *p= &stu1.num; 结构体变量的地址主要用于作函数参数,传递结构体变量的地址;
注5、在定义结构体变量的同时,可以进行初始化。注5、在定义结构体变量的同时,可以进行初始化。 struct student { int num; char name[20]; char sex; char addr[30]; }stu={15001, "宋 红", 'M', "123 Road Beijing"}; 注意:结构体变量的各个初值用大括号{、}括起来,大括号内各个成员变量的值之间用逗号分隔,其值必须与成员变量一一对应,且数据类型应与成员变量一致。 例7.1:输入5名学生的学号及成绩,求成绩最好的学生的学号及成绩。 Eg902.cpp
7.4 结构体数组与程序举例 数组的每个元素都是结构体类型的数据,它们分别包含各个成员项。 7.4.1 结构体数组的定义 1、先定义结构体类型,再定义结构体数组 struct student { int num; float score; }; struct studentstu[30]; 2、定义结构体类型的同时定义结构体数组 struct student { int num; float score; }stu[30]; struct { int num; float score; }stu[30]; 3、直接定义结构体数组
7.4.2 结构体数组的初始化 一般形式: struct 结构体类型标识符 { 类型标识符1 成员名1; 类型标识符2 成员名2; …… …… 类型标识符n 成员名n; }结构体数组={ {数组元素0的各个初值}, {数组元素1的各个初值}, ……}; 例: struct student { int num; char name[20]; float score; }stu[3]={{10101,"Li Ming", 88},{10102,"Zhang Jin",92}, {10103, "Wang Lin", 98.5}};
说明: 1、初始化时,数组元素的个数可以不指定,系统会根据初值的结构体常量的个数来确定数组元素的个数; struct student { int num; char name[20]; float score; }stu[ ]={{10101,"Li Ming", 88},{10102,"Zhang Jin",92}, {10103, "Wang Lin", 98.5}}; student stu1[ ]={{10101,"Li Ming", 88},{10102,"Zhang Jin",92}, {10103, "Wang Lin", 98.5}}; 2、数组中各个元素的初值用大括号{、}括起来,同一数组元素的各个成员变量的初值用逗号分隔。必须在初始化时完成。
7.4.3应用举例 例7-1、若有如下定义: struct aa { long num; char name[20]; }; ①、要给stu[1]输入18、990001、"zhang hua bin",写出所用到的语句; ②、如何将stu[1]中的名字复制到stu[0]中的相应位置上? struct bb { int age; struct aa first; }stu[2]; ③、输出stu[]的值。 (1)定义时初始化: bb stu[2]={{0,{0,' '}},{18,{990001,"zhang hua bin"}}}; (2)程序中初始化 Eg903.cpp
7.4.3应用举例 //li7-1.cpp #include<iostream.h> #include<String.h> struct aa { long num; char name[20]; }; void main() { stu[1].age=18; stu[1].first.num=990001; strcpy(stu[1].first.name,"zhang hua bin"); strcpy(stu[0].first.name,stu[1].first.name); struct bb { int age; struct aa first; }stu[2]; for(int i=0;i<2;i++) cout<<stu[i].age<<stu[i].first.num<<stu[i].first.name<<endl; } 运行结果: 00zhang hua bin 18990001zhanghua bin Eg903.cpp
7.4.3应用举例 • 例7-2:结构数组的输入与输出。 • 说明头文件 • (2)定义结构类型Person • (3)定义结构数组变量 • Person a[10];//全局 (6)主函数的设计 定义并输入记录个数n 调用各函数(或模块)完成相应功能 程序清单(P197):li7-2.cpp (4)定义输入函数 void input (int n)//n为记录个数 (5)定义输出函数 void output(int n) 注:性别的输入/输出sex为逻辑变量。 Eg903.cpp
7.4.3应用举例 • 例7-3:从结构数组查找最大值。 • 说明头文件 • (2)定义结构类型Person • (3)定义结构数组变量并初始化 • Person a[5]={…};//全局 (5)主函数的设计 定义并输入记录个数n 调用output(int n),输出所有记录 调用find(int n),查找最大值并 输出此记录 程序清单(P198) :li7-3.cpp (4)定义查找函数 void find (int n)//n为记录个数 注1:查找后输出,不须返回值 查找关键字为,结构中的工资,即查找工资最大的记录。 注2:x存放最大值,初值为 x=a[0].pay; Eg903.cpp
7.4.3应用举例 • 例7-4:对结构数组记录排序。 • 结构数组排序 • 正序(升序)、逆序(降序) • 排序方法:如选择法、起泡法 • 关键字:排序码 • 结构数组排序:按关键字,将数组中各条记录进行排序。 • (2)选择法排序 • 从n个元素中找出最小值,与a[0]交换; • 从a[1]~a[n-1]中找出最小值,与a[1]交换 • 以此类推,进行第n-1趟时将a[n-2]和a[n-1]比较排序。 Eg903.cpp
7.4.3应用举例 n-1 注1:查找替换次数: 注2:k指向每趟中当前具有最小排序码的元素(下标值)。 注3:交换时,对整个记录进行交换,而不是只对关键字交换 注4:一趟搜索中k的值未变化,则不发生交换。 (3)程序及说明(P201):li7-4.cpp 思考1:将结构数组按排序码升序排列。但不改变结构数组排列顺序。程序如何设计。 解决:定义一个数组,存放按排序码顺序排列的下标值。P202 思考2:排序过程的指针实现? Eg903.cpp
例7-5:用指针数组实现排序。 1. 指向结构体数组的指针 指向结构体变量的指针变量,可以指向结构体变量,也可以指向同类型的结构体数组的元素。 struct student { int num; char name[30]; float score; }stu[30], *p; p=stu; p=&stu[3]; p=&stu[2].num;//error p只能指向一个struct student类型的数据(某个元素的的起始地址),不能指向一个成员变量。 可以int *q= &stu[2].num 注意: ①、若p的初值为stu,p+1后指向下一元素的起始地址; (++p)->num 先使p自加1,然后得到它指向元素的num成员值 ++p->num 使p指向的结构体变量中的成员num的值加1 (p++)->num 先得到p->num的值,然后使p自加1,指向stu[1] p->num++ 使p指向的结构体变量中的成员num的值加1 ②、若p已定义为指向结构体类型的数据,则p只能指向相应类型的变量或数组元素,不能指向数组元素中的某一成员; Eg905.cpp
2. 结构体数组的指针实现结构数组排序 设结构数组用a表示,结构指针数组用b表示,b中每个元素b[i]的初值为&a[i]。则b为指针数组,它的每一个成员是一个指针,指向a数组的的一个结构元素。 用结构体数组指针访问结构体数组成员: 如:b[i]->grade;//b[i]指向的结构数组元素中的grade成员。 程序说明(P205) :li7-6.cpp: ①、结构体数组指针的定义 Student **b; *b=new Student *[n];//动态分配一个具有n个元素的 //结构指针数组 ②、结构体数组指针的赋值 for (i=1;i<n;i++) b[i]=&a[i]; Eg905.cpp
7.5 结构体指针作为函数参数 将一个结构体变量的值传递给另一个函数,有3种办法: ①、用结构体变量的成员作参数: 将实参值传递给形参,属于“值传递”方式(注意实参与形参的类型保持一致)。 void fun(int); fun(stu.num); ②、用结构体变量作实参: 采取的是“值传递”方式,将结构体变量所占的内存单元的内容全部顺序传递给形参。要求形参也必须是同类型的结构体变量。 例1:P206 函数:int search(Student s[],int n,Student x) 调用:Student a[5]={…};//定义结构体数组 Student x={…};//定义结构体变量 search(a,5,x); 程序:P206 Eg906.cpp
7.5 结构体指针作为函数参数 例2:从Student结构数组中更新某一给定学号的记录,若成功则返回1,否则将记录插入到数组未尾并将数组长度加1,返回0 分析: 函数名update,函数类型可为整形或布尔型。 参数:结构指针 s,值传递 数组长度n,引用类型,带回长度值。 存储更新值的结构变量x;const 引用类型,调用过程中不须 修改; 注:x用引用类型,可以直接访问实参空间,不须重新分配内存。 思路: 从数组s第一个元素起顺序查找,若找到s[i].num与x.num相等,则用x更新 s[i]并返回1,否则,将x插入到s[n]的位置,将n增1后返回0。 程序:P208 函数定义:int update(Student s[],int &n,const &x); 调用: update(a,n,x); Eg906.cpp
7.5 结构体指针作为函数参数 ③、用指向结构体变量(或数组)的指针作实参: 将结构体变量(或数组)的地址传给形参,如 Void fun(Student *p,n); Student x,*p=&x; fun(p,n); 例3:从Student结构数组中查找学号等于给定值的记录时,若查找成功返回该元素的指针,否则返回空。 分析: 函数名search,函数返回值类型为结构指针Student *,即被检索元素的地址。 参数:结构指针 Student s[]或Student *P[],值传递 数组长度n,引用类型,带回长度值。 存储更新值的结构变量x;const 引用类型,调用过程中不须 修改; 注:x用引用类型,可以直接访问实参空间,不须重新分配内存。 程序:P209 函数定义:int search(Student s[],int n,const Student &x); 调用: search(a,n,x); Eg906.cpp
7.6 结构与链表 结构中的指针域可以指向本身结构类型,这种类型的对象称为结点,每个结点的指针域指向下一个结点,形成链表。 struct IntNode {int data;IntNode* next;}; 1.几个概念: 表头指针:指向第一个结点的指针 如f指针; 表头结点:第一个结点,值为48的结点; 后继结点:每个结点指针域所指向的结点 前驱结点: 表尾结点:指针域为空 Eg906.cpp
7.6 结构与链表 2、建立链表 链表中的结点通常通过动态分配产生IntNode* p1=new IntNode; 向表尾插入新结点: void create(IntNode*&f, int n) {int x;cin>>x; f=new IntNode; f->data=x;f->next=Null; //建立表头结点 IntNode *p=f;//p指向表头结点 fr (int i=1;i<=n-1;i++) cin>>x;p->next=new IntNode; p=p->next;p->data=x; } p->next=NULL; } 建立一个链表:向表头插入新结点void create(IntNode*&f, int n) {f=NULL;while(n-->0) {IntNode* p=new IntNode;cin>>p->data;p->next=f;f=p;}} 注:f 引用调用,指向表头结点 Eg906.cpp
7.6 结构与链表 3、遍历一个链表 链表具有顺序存取特性:当访问一个链表时,必须从表头指针出发顺序进行。 void print(IntNode*f) {while(f!=NULL) {cout<<f->data<<‘ ’; f=f->next;}}void main() {IntNode* p1; create(p1, 5); //返回表头结点指针p1cout<<endl; print(p1);}// 输入 1 2 3 4 5 输出 5 4 3 2 1 : 程序:P210 Eg906.cpp
7.7 结构与操作符重载 C++有两种形式的重载: 1. 函数重载:相同作用域内,若干参数特征不同的函数使用相同的函数名。 2. 运算符重载:同样的运算符可以施加于不同类型的操作符上面,提高语言的灵活性和可扩充性,实现多态。 C++操作符不仅能够用于预定义数据类型,经操作符重载函数定义后,也可以用于用户自定义数据类型。 如:分数结构类型 struct Fran {int nu;int de;} 若:定义 Fran a={2,3}, b={3,4},c; 欲实现 c=a+b={17,12} ? Eg906.cpp
7.7.1 函数实现 Fran FranAdd(const Fran& a,const Fran& b) {Fran c; c.nu=a.nu*b.de+b.nu*a.de; c.de=a.de*b.de; FranSimp(c) ;//对结果进行简化处理。 Return c; } 函数调用: C=FranAdd(a,b); void FranSimp(Fran& x) {int m,n,r; m=x.nu;n=x.de; r=m%n; While(r!=0) {m=n;n=r;r=m%n;}//求最大公约数 if(n!=1) {x.nu/=n;x.de/=n;}//化简 if(x.de<0) {x.nu=-x.nu;x.de=-x.de;}//分母为负处理 } 思考:判断两个分数是否相等。 写出函数重载运算符 程序举例:P215 Eg906.cpp
7.7.2 操作符重载函数实现 欲将上述运算写成:c=a+b,如何实现。 使用操作符重载函数operater Fran operator+(const Fran& a,const Fran& b) {Fran c; c.nu=a.nu*b.de+b.nu*a.de; c.de=a.de*b.de; FranSimp(c) ;//对结果进行简化处理。 Return c; } 调用:c=a+b; 双目操作符重载函数的定义格式: <返回类型> operator<双目操作符>(<参数说明1>,<参数说明2>) 调用:<参数1> <双目操作符> <参数2> Eg906.cpp
7.7.2 操作符重载函数实现 单目操作符重载函数的定义格式: <返回类型> operator<单目操作符>(<参数1>) 调用:<单目操作符> <参数2> 例: ++x Fran operator++(const Fran& x) { x.nu+=x.de; Returnx; //x先加1,返回x的引用 } 例:x++ Fran operator--(const Fran& x,int) { Fran y=x; x.nu+=x.de; Return y; //使x加1,返回x的原值 } Eg906.cpp
7.7.3 输入输出重载实现 1.用函数实现结构数据类型的输入输出 void FranInput(Fran& x) {char ch; Cin>>x.nu>>chx.de; if(x.de==0) {Cerr<<“除数为0”<<endl; Exit(1); } } 思考:写出输出函数,并实现对负数、分子为0的处理。 Eg906.cpp
7.7.3 输入输出重载实现 2.重载>>运算符 左操作数是标准输入流类型istream对象,如cin 右操作数是自定义结构类型变量: istream& operator>>(istream& istr,Fran& x) {char ch; istr>>x.nu>>chx.de; if(x.de==0) {Cerr<<“除数为0”<<endl; exit(1); } return istr; } Eg906.cpp
7.7.3 输入输出重载实现 3.重载<<运算符 左操作数是标准输出流类型ostream对象,如cout 右操作数是自定义结构类型变量: ostream& operator>>(ostream& ostr,Fran& x) {char ch; ostr<<x.nu<<‘/’<<x.de<<endl; return ostr; } 例1主函数:P218 例2:P219 Eg906.cpp
i ch f 2000 7.8 联合(共用体) 有时候需要将几种不同类型的变量存放到同一段内存单元中。 可以把一个整型变量、一个字符型变量、一个实型变量放在同一个地址开始的内存单元中。即使几个不同的变量共占同一段内存空间。 所谓“共用体(union)”是指使几个不同的变量共占同一段内存的数据类型。 7.8.1 共用体的定义 1、先定义共用体类型标识符,再定义变量 union共用体类型标识符 { 类型标识符1 成员名1; 类型标识符2 成员名2; …… …… 类型标识符n 成员名n; }; union data { int i; char ch; float f; }; union共用体类型标识符变量名表; union data a, b, c;
2、直接定义共用体变量 union共用体类型标识符 { 类型标识符1 成员名1; 类型标识符2 成员名2; …… …… 类型标识符n 成员名n; }变量名表; union data { int i; char ch; float f; }a, b, c; union { int i; char ch; float f; }a, b, c; 3、共用体与结构体的定义形式相似,但含义不同: 结构体变量所占的内存长度等于各成员所占的内存长度之和。(每个成员分别占有自己的内存) 共用体变量所占的内存长度等于最长的成员的长度。(每个成员分别占有自己的内存) 4 sizeof(union data)=
7.8.2 共用体变量的引用 1、引用形式为: 共用体变量名.成员名 union data { int i; char ch; float f; }a, b, c; a.i 引用共用体变量a中的整型变量i a.ch 引用共用体变量a中的字符变量ch a.f 引用共用体变量a中的实型变量f 2、注意: 不能直接引用共用体变量,而只能引用共用体变量的成员 printf("%d", a); printf("%d", a.i); printf("%d", a.ch); 3、共用体类型数据的特点: ①、同一内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放几种;
也就是说,每一瞬时只有一个成员起作用,其它的成员不起作用,即不是同时都存在和起作用。也就是说,每一瞬时只有一个成员起作用,其它的成员不起作用,即不是同时都存在和起作用。 ②、共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用; a.i=1; a.ch='a'; a.f=1.5; ③、共用体变量的地址和它的各成员的地址是同一地址; &a, &a.i, &a.ch, &a.f都是同一地址 ④、不能对共用体变量名赋值;也不能企图引用变量名来得到成员的值;不能在定义共用体变量时进行初始化; union { int i; char ch; float f; }a={1, 'a', 1.5}; a=1; m=a; ⑤、不能把共用体变量作为函数参数,也不能让函数带回共用体变量,但可以使用指向共用体变量的指针;
⑥、共用体作为一种数据类型,可以在定义其它数据类型中使用。可以将结构体变量的某一成员定义为共用体类型,也可以定义共用体数组。⑥、共用体作为一种数据类型,可以在定义其它数据类型中使用。可以将结构体变量的某一成员定义为共用体类型,也可以定义共用体数组。 例:设有若干人员的数据,其中有学生和教师。学生的数据包括:姓名、学号、性别、职业、班级。教师的数据包括:姓名、教师号、性别、职业、职务。现要求把它们放在同一表格中。 int num; union { int class; char position[10]; }category; char name[20]; char sex; char job;
li2 scanf("%c", &per[i].job); if (per[i].job=='s') scanf("%d", &per[i].category.class); else if (per[i].job=='t') gets(per[i].category.position);
7.8.3 使用联合举例 例1: struct person { int num; char name[20]; char sex; char job; union { int class; char position[10]; }category; }per[30]; 例2:P226
§9.4位段 位段:在一个结构体中以位为单位来指定其成员所占的内存长度。可以认为,位段是一种特殊的结构体类型。 1、位段定义的一般形式: struct结构体类型标识符 { unsigned位段名1:长度1; unsigned位段名2:长度2; … … unsigned位段名n:长度n; }; struct { unsigned a:2; unsigned b:6; unsigned c:3; unsigned d:1; }data; 2、位段的引用: 变量名.成员名
①、赋值: struct { unsigned a:2; unsigned b:6; unsigned c:3; unsigned d:1; }data; data.a=1; data.b=10; 注意位段允许的最大值范围。 data.a=5; 自动取赋予它的数的低位 data.a=1; 101 ②、位段可以在数值表达式中引用, 自动转换为整型; data.b++; data.c=data.a+2; ③、可以用整型格式输出; printf("a=%d b=%d c=%d d=%d\n", data.a, data.b, data.c, data.d); 3、关于位段的定义和引用的几点说明: ①、位段成员的类型必须指定为unsigned类型; ②、一个位段必须存储在同一存储单元,不能跨两个单元。即一个位段的长度不能大于一个存储单元的长度。
a c b ③、若某个位段开始存放时,相应的存储单元空间不够,则剩余空间不用,而从下一个单元开始存放; struct { unsigned a:4; unsigned b:2; unsigned c:6; }data; 此位段无名,该3位闲置不用。 ④、可以定义无名位段: struct { unsigned a:4; unsigned :3; unsigned b:2; unsigned c:6; }data; struct { unsigned a:4; unsigned :3; unsigned b:2; unsigned :0; unsigned c:6; }data; ⑤、可以定义长度为0的无名位段,作用是使下一个字段从下一个存储单元开始存放; ⑥、不能定义位段数组。
§9.6 枚举类型 9.6.1 枚举类型的定义 所谓“枚举”是指将变量的所有取值一一列举出来,变量的值只限于列举出来的值的范围内。 枚举类型变量的定义: 1、先定义枚举类型标识符,再定义变量 enum枚举类型标识符{枚举元素1, 枚举元素2, ……, 枚举元素n}; enum枚举类型标识符变量列表; enum person{Man, Woman}; enum person x; enum weekday{Sun, Mon, Tue, Wed, Thu, Fri, Sat}; enum weekday workday, weekend;
2、直接定义枚举变量 enum枚举类型标识符{枚举元素1, 枚举元素2, ……, 枚举元素n}变量列表; enum person{Man, Woman}x; enum weekday{Sun, Mon, Tue, Wed, Thu, Fri, Sat} workday, weekend; enum BOOL{FALSE, TRUE}; 3、说明: Sun、Mon、……、Sat等称为枚举元素或枚举常量,它们是用户自定义标识符。这些标识符并不自动的代表什么含义,用什么标识符代表什么含义,完全由程序员决定,并在程序中作相应的处理。
9.6.2 枚举变量的引用 1、在C编译时,对枚举元素按常量处理,故称枚举常量。它们不是常量,不能对它们赋值。 Sun=0; TRUE=1; 2、枚举元素作为常量,它们是有值的。C语言编译时按定义时的顺序使它们的值为0、1、2、…… enum weekday{Sun, Mon, Tue, Wed, Thu, Fri, Sat}workday; workday=Mon; printf("%d", workday); 3、可以在定义时改变枚举元素的值 enum weekday{Sun=7, Mon=1, Tue, Wed, Thu, Fri, Sat}workday; enum weekday{Sun=7, Mon, Tue, Wed=0, Thu, Fri, Sat}; 4、枚举值可以用来做判断比较 if (workday==Mon) …… if (workday>Sun) ……
5、一个整数不能直接赋给一个枚举变量 workday=2; workday=(enum weekday)2; workday=(enum weekday)(x-y); 例:从键盘输入0~6的任意整数,0表示星期日,1~6分别表示星期一到星期六,要求编写程序输出对应的英文名称。 enum weekday{Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}week; scanf("%d", &i); switch(i) { case 0: week=Sunday; break; case 1: week=Monday; break; case 2: week=Tuesday; break; case 3: week=Wednesday; break; case 4: week=Thursday; break; case 5: week=Friday; break; case 6: week=Saturday; break; } switch(week) { case Sunday: …… break; case Monday:…… break; case Tuesday: …… break; case Wednesday: …… break; case Thursday: …… break; case Friday: …… break; case Saturday: …… break; }