1 / 42

九、联合 union 十、数据的引用类型转换 十一、位域或位字段

第 12 章 结 构 与 联 合. 九、联合 union 十、数据的引用类型转换 十一、位域或位字段. 九、联合 union 1. 联合的特性和定义 由关键字 struct 声明的数据描述称为结构类型而 union 或 class 声明的数据描述称为联合类型或类类型 , 分别简称为 结构和联合或类。 结构、联合、类是集合数据类型。 int 、 float 等是系统预先取好的可以直接使用的类名, 关键字 struct 和 union 或 class 后紧跟的标识符则是用户引入 的类名,需要事先加以声明。.

coty
Download Presentation

九、联合 union 十、数据的引用类型转换 十一、位域或位字段

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第12章 结 构 与 联 合 • 九、联合union • 十、数据的引用类型转换 • 十一、位域或位字段

  2. 九、联合union • 1.联合的特性和定义 • 由关键字struct声明的数据描述称为结构类型而union • 或class声明的数据描述称为联合类型或类类型,分别简称为 • 结构和联合或类。 • 结构、联合、类是集合数据类型。 • int、float等是系统预先取好的可以直接使用的类名, • 关键字struct和union 或class后紧跟的标识符则是用户引入 • 的类名,需要事先加以声明。

  3. 在程序设计中常出现非此即彼的数据,这些数据的类型在程序设计中常出现非此即彼的数据,这些数据的类型 • 可以不同,需要将其放置在同一片内存。 • 关键字union 建立的数据类型为用户引入的联合类型, • 简称联合。联合一方面是节省内存空间,另一方面是特殊的 • 编程考虑。 • 联合的特性和定义: • 联合类型定义的变量称为联合变量。指向联合变量的指 • 针简称为联合指针。联合类型的声明和联合变量的定义采用 • 与结构类似的语法,不同的是联合使用关键字union。

  4. 将前面关于结构声明复制过来略加变动并用union替代将前面关于结构声明复制过来略加变动并用union替代 • struct得到: • union UnionName 联合 联合名 • { type member; { 数据类型 成员名 ; • type2 member2; 数据类型2 成员名2; • typen membern; 数据类型n 成员名n; • }; }; • 关键字union用于声明同一片内存可以存放不同类型的 • 数据,但在一个时刻只有某一类型的数据处于有效的状态。

  5. 联合类型声明中的数据成员具有如下的性质: • a.不同的成员占有起始地址相同的内存区域,这些成 • 员以并排的方式重合在一起。 • b.该内存区域的长度确定为所有成员中占有内存空间 • 最长的成员所对应的长度。 • c.在一个具体的时刻仅包含一种数据成员有效虽然成 • 员可以是集合型的数据。 • d.初始化联合变量时默认对第一个成员进行赋值亦仅 • 对一个成员赋值,初始值的类型属性应与第一个成员的类型 • 属性一致,否则编译器至少给出警告。 • e. C++中联合不参入继承机制即联合类既不作基类也不 • 作派生类,不能有虚拟函数。

  6. [例]联合的内存和结构的内存大小比较 • #include<stdio.h> • typedef struct s_a { double d; char c[9]; } A; • typedef struct s_b { double d; long k[2];} B; • typedef union u_t { A a; B b; } U; • typedef struct s_t { A a; B b; } S; • void main (void) • { printf ("sizeof(A)= %d,", sizeof (A)); • printf ("sizeof(B)= %d,",sizeof (B)); • printf ("sizeof(U)= %d,", sizeof (U)); • printf ("sizeof(S)= %d", sizeof (S)); • } //输出:sizeof (A)= 24,sizeof (B)= 16,16, • sizeof (U)= 24,24,sizeof (S)= 40

  7. 联合一经声明就可以定义联合变量、联合数组和联合指联合一经声明就可以定义联合变量、联合数组和联合指 • 针。 箭头运算符“->”与圆点运算符“.”可以用来访问联合中 • 的成员,箭头运算符"->"的左侧是联合指针,圆点操作符".“ • 的左侧是联合变量,右侧是联合中的成员。 • 这和结构变量的使用规则一致。 • 不同的是结构变量拥有结构中各独立的成员所占内存之 • 和; • 联合变量则仅是最大成员所拥有的内存,这一片内存可 • 由若干类型属性不同的成员适当索引,它们齐头地占有同一 • 块内存。

  8. 联合遵循先声明后定义再使用的次序。如下所示:联合遵循先声明后定义再使用的次序。如下所示: • typedef union u_t {int k[4]; long member; float y; }U; • U obj, *pobj; • obj.member //联合变量名.成员名 • pobj->member //联合指针名->成员名 • 三个步骤可以合为一体,再加上初始化格式为: • union u_t { int k[4]; long member; float y; } • b = {1,2,3,4}; • 初始化只对第一个联合成员进行。该凝练的格式声明一 • 个名为u_t的联合名,其拥有三个并置的成员,第一个是int • 型的数组成员k[4]、第二个是long型的成员member,第三 • 个是float型的成员y.这三个成员的内存起始地址是一样的。

  9. [例]联合指针入口形参和联合引用返回 • #include<stdio.h> • inline int f(int k) {return k;} • typedef union u_t { char* s; int (*f)(int); int d; } Un ; • Un& initial (Un*p ,int n,char * s) • { switch (n) • { case 1: p->s=s; break; • case 2: p->f=f; break; • default: p->d=n; break; • } //三个成员互斥地使用同一内存 • return *p; • } • void main (void) • { Un x; • for (int n=1;n<4;n++)

  10. { Un y= initial (&x,n,"abcd"); • switch (n) • { case 1: printf ("[%s,%s]\t", x.s,y.s); break; • case 2: printf ("[%d,%d]\t",x.f(1),y.f(2)); break; • default: printf ("[%d, %d]\n", x. d,y.d); break; } • } • } //输出:[abcd,abcd] [1,2] [3,3] • 联合变量、联合指针和联合引用可以作为形参,联合变 • 量可以相互赋值。 • 对联合变量的操作需要特别注意成员的类型属性,不同 • 类型属性的成员应由不同的分支处理。

  11. 2.联合的内存映像 • 考虑如下初始化于一体的联合声明和联合变量的定义: • union u_t { int k[4]; long z; float y; } • b={1,2,3,4},c; • 声明了一个联合名为u_t的联合类型,具有三个成员, • 这三个成员是int型数组k[4],long型成员z,float型成员y。 • 第一个成员是数组成员k。同时定义了两个联合变量b • 和c,在定义联合变量b的时候对其第一个成员进行了初始化 • 处理。 • 该联合占有的内存空间是: • sizeof (b)=sizeof (union u)= sizeof (int[4])= • sizeof (b.k)=16

  12. k[0] k[1] K[2] K[3] • 该联合在32位编程模式下的内存布局如下: • int型数组k[4] • long型成员z 8 12 16 • float型成员y 4 联合的数据内存分布 • 在PC微机上数据的存放方式是低尾端形式的, 即short • 型16位字节的低8位存放在内存的低地址处, 高8bit存放在内 • 存的高地址处高尾端的存放方式则相反。 • 下页的例子说明数据在PC内存中的顺序是低尾端形式。

  13. [例]强制类型转换显示低尾端的存储格式(8位二进制数的低[例]强制类型转换显示低尾端的存储格式(8位二进制数的低 • 位在右边,高位在左边). • #include<stdio.h> • void main() • { unsigned long m = 0x87654321UL; • unsigned char * p=(unsigned char*)&m; • for (int i=0; i<sizeof (long);i++) • printf ("%x ",*p++); • }

  14. 21 43 65 87 00100001 01000011 01100101 10000111 • 上例低尾端的PC计算上输出结果:21 43 65 87 • 这样32位整数的8个 4位二进制数的十六进制数数码表示 • 为: • m= h7h6h5h4h3h2h1h0 =87654321 • 该数以字节即8位bit为最小内存寻址单位的内存存储格 • 式为(低地址标注在左边): • 16进制表示: • 低地址 高地址 • 2进制表示: • 低地址 高地址

  15. 联合的同一片内存可以通过不同的名称索引。对一个成联合的同一片内存可以通过不同的名称索引。对一个成 • 员的改变直接影响联合中的其它成员的数据状态,对数据的 • 解释取决于数据的存储格式和模块转换。 • [例]联合的内存布局和内存数据的解释 • #include<stdio.h> • typedef union u_t { char c[2]; unsigned long k; } U; • void main() • { U x = {3,1}; //x.c[0]=3;x.c[1]=1; 潜在地导致x.c[2]=0;x.c[3]=0; • printf ("%d,0x%08x\t",x.k,x.k); • x. k= 0x67686162UL; • printf ("%c,%d,%c,%d; ",x.c[0],x.c[0],x.c[1],x.c[1]); • printf ("%c,%d,%c,%d\n ",x.c[2],x.c[2],x.c[3],x.c[3]); • }

  16. d3 00000000 c[3] c[3] c[2] d2 00000000 c[2] c[1]0x61'a' d1 00000001  0x61 c[0]0x62'b' d0 000000011  0x62 0x67 0x68 c[1]=100000001 c[0]=300000011 • 说明:占4字节的整型数 (4个8位的十六进制数)存贮格 • 式在微机上是低尾端格式,具有如下的形式(低地址标注在 • 边,8位二进制数的低位在右边): • 高地址 低地址 • k= 0x0000|0103  1*16*16+3=259 • x.k= 0x67686162UL; • 高地址 低地址

  17. [例]浮点数和整型数内存存储格式不同 • #include<stdio.h> • typedef union u_fl { float f; long k; } Ua; • typedef union u_il { int f; long k; } Ub; • struct Sab { union {float f ; long k;} a; Ub b;} s={ 1.0,2 }; • void main() • { printf("[%1.0f,%d]\t",s.a.f,s.b.f); • Ua & x=(Ua &)s.a ; x.f=10; • printf ("[%d,%d]\t", (long)(x.f+s.a.f), x.k); x.k=20; • printf ("[%d,%d]\t", (long)x.f, x.k+s.a.k); • Ub & y=s.b ; • y.f=10; printf ("[%d,%d]\t",(long)y.f,s.b.k); • y.k=20; printf ("[%d,%d]\n",(long)s.b.f,y.k); • } //输出:[1,2] [20,1092616192] [0,40] [10,10] [20,20]

  18. 联合将不同类型的数据锁定在一块起始地址相同的内存, • 通过不同的成员名称或别名来索引内存,以多种方式解释同 • 一内存数据。 • 联合具有特殊的数据强制类型转换的能力。 • 注意: • union {float f ; long k;} a; 是联合类型直接定 • 义变量,此时联合是无名的。但这种格式不减少访问内层成 • 员的层次 。

  19. 3.无名联合 • 无名联合在声明时不带联合名,这种声明在C++语言 • 中具有特殊的含义。 • 在无名联合中定义的名称超出定界的一对花括号之外, • 不能跟同一作用范围其它的变量名冲突,不能有成员函数。 • 在全局范围定义的无名联合必须声明为静态的,结构中的无 • 名联合其成员访问控制属性是公共的,不允许存在私有的成 • 员。 • a.局部的和全局的无名联合 • 局部范围的无名联合实际上定义的是内存共享的变量, • 这些不同类型的变量拥有相同的起始内存地址,内存数据的 • 有效性取决于最新的二进制状态和对上下文环境的理解。

  20. [例]无名联合直接定义局部共享的多个变量n,m, x, y。 • #include<stdio.h> • void main (void) • { union { int n; long m; double x; double y; } ; • printf ("[%p,%p]\t",&n,&y); • n=3; printf ("[%d,%d]\t",n,m); • x=6; printf ("[%f,%f]\n",x,y); • } • //输出: • [0065FDF0,0065FDF0] [3,3] [6.000000,6.000000]

  21. [例]无名联合 static union{ point_t z; point3d b;};定 • 义静态全局结构变量b和z • # include <stdio.h> • typedef struct { double v[3]; } point_t; • typedef struct { double x,y,z; } point3d; • typedef union { point_t z; point3d b;} Ua ; • point3d* CrossProduct ( point3d in1, point3d& a) • { const point3d in2=a; • a.x = in1.y * in2.z - in1.z * in2.y; • a.y = in1.z * in2.x - in1.x * in2.z; • a.z = in1.x * in2.y - in1.y * in2.x; • return &a; • }

  22. static union { point_t z; point3d b; }; • extern point_t * q; • const point3d x={1,1,0}; • point_t* q; • void main(void) • { b.x=0; b.y=2; b.z=1; • q= (point_t*)CrossProduct (x,b); • if ( b.x==z.v[0] && b.y==z.v[1] && b.z==z.v[2]) • printf ("b={%4.1f,%4.1f,%4.1f}\t", b.x, b.y,b.z); • printf ("q={%4.1f,%4.1f,%4.1f}\t", q->v[0], • q->v[1], q->v[2]); • Ua a={1,2,3}; • printf ("a={%4.1f,%4.1f,%4.1f}\n", a.b.x, a.b.y,a.b.z); • }

  23. //输出: • b={ 1.0,-1.0, 2.0} q={ 1.0,-1.0, 2.0} a={ 1.0, 2.0, 3.0} • 上面的无名联合定义静态全局结构变量b和z,b和z 是 • 同一片集合内存的两个别名,相应的结构成员也一对一的彼 • 此相配。 • 两个结构本身描述的是空间点的坐标,只是成员名称 • 不同;将这样的结构变量联合在一起达到内存数据充分共享. • 静态的全局变量可以通过外部连接属性的全局指针在不 • 同模块中传递信息。 • 联合可用于接口设计,两个课题组建立了相同的数据结 • 构例如 :point_t和point3d,只是其中的成员名称不同,可 • 以将其联合在一起。

  24. b.结构范围中的无名联合 • 可以在结构的声明中引入无名联合,无名联合包含的数 • 据成员在内存中共享一片内存空间,无名联合中单独的成员 • 直接作为结构成员的相对独立部分。联合变量可以作为结构 • 的成员,这相当于嵌入的对象,对于嵌入对象需要层层访 • 问。无名联合减少访问成员的层次,可以直接访问无名联合 • 中的成员。联合提供一种课题组之间相同数据结构(即成员 • 个数、类型、次序相同只是名称不同)的接口技术。设课题 • 组A和B的结构类型为: • typedef struct person_a • { int n; float f; char *s; } A; • typedef struct person_b • { int number; float income; char * name; } B;

  25. A结构和B结构是一致的。并在相关的结构上相应地开 • 发一套算法。将这两个结构通过无名联合并置在一起,则可 • 以发挥各自的特点。下面的例子说明无名联合的这一用法。 • [例]结构范围中的无名联合实现不同课题组之间的数据接口 • # include<stdio.h> • # include<string.h> • typedef struct a_t • { union { int n; int number; }; • union { float f; float income; }; • union { char *s; char *name; }; • } A, B;

  26. int SeqFind(const B s[ ],int n,float key) • { for(int i=0;i<n;i++) • if (s[i].income==key) return i; • return -1; • } • int SeqFind (const A s[ ],int n,const char* key) • { for (int i=0;i<n;i++) • if (strcmp(s[i].s,key)==0) return i; • return -1; • }

  27. void InitData (B b[ ],int n[ ],float f[ ], • char (*s)[20],int num=5) • { for(int i=0;i<num;i++) • { b[i].number=n[i]; • b[i].income=f[i]; • b[i].name =s[i]; • } • } • void show (B& r) • { printf ("{%d,%f,%s}\t",r.number,r.income,r.name); } • void show (A* s,int n) • { for(A*p=s; p<s+n; p++) • printf ("{%d,%4.1f,%s},",p->n,p->f,p->s); • printf ("\n"); • }

  28. const int N=5; • static char • ca[N][20]={"Hisen","Boir","Rose","Bush","Kelin"}; • void main() • { static int na[N]={11,22,33,44,55}; • float xa[N]={88,90,70,80,60}; A sa [N]; • InitData(sa,na,xa,ca,N); show (sa,N); • int k=SeqFind (sa,N,"Rose"); if (k!=-1) • show (sa[k]); • k=SeqFind (sa,N,60); • if (k!=-1) • show (sa[k]); • }

  29. //输出结果: • {11,88.0,Hisen},{22,90.0,Boir}, • {33,70.0,Rose},{44,80.0,Bush}, • {55,60.0,Kelin},{33,70.000000,Rose} • {55,60.000000,Kelin} • 联合可实现内存的共用与数据的共用,更可实现不同 • 类型数据互相排斥地占用同一内存或相同内存段互异数据的 • 不共用。 • union在一些介绍C语言的中文书中被译为共用体,这 • 多少有失union原有的丰富内涵。 • 术语翻译应切近原意,而其确切含义则需详细解释。

  30. 十、数据的引用类型转换 • 基本变量之间存在类型转换关系。 • 例如: • float f; long l=7788; f=(float)l; l=(long)f; • 表达式语句[f=(float)l;]意味着将long型变量l的值在相 • 应的内存单元取出,经过类型转换模块,然后将结果送入f • 变量表示的存储单元中,以浮点格式存储。 • 对于同类型的结构变量或联合变量编译器允许赋值运 • 算。

  31. 设存在两个结构的声明为: • struct sb {sb结构数据成员列表;} ; • struct sa {sa结构数据成员列表;} ; • 定义结构变量a,a1,b,b1分别如下: • struct sa a,a1; struct sb b,b1; a=a1; b=b1; • 赋值表达式语句a=a1表示结构变量a1的数据状态赋给 • 对应的结构变量a,相当于系统进行了函数调用: • memcpy (&a,&a1,sizeof(a));

  32. 但不同类型的结构变量的赋值如 a=b1 则是不允许的。 • 因为编译器并未提供不同类型结构变量之间赋值的缺省 • 运算。 • 同样对于类型转换: a=(struct s)b1; • 系统也会提出错误警告。系统对于不同结构变量的数值 • 转换无论是隐含的或显式都没有提供缺省的保证。但C++允 • 许引用形式的类型转换。 • 引用形式类型转换的一般形式为: • (目标类型名&)源变量 • (type&)variable • 引用类型转换的结果为左值。

  33. 对于两个结构变量a,b,一个具体的引用类型转换的语对于两个结构变量a,b,一个具体的引用类型转换的语 • 法格式如下: • a = (struct sa&)b; (struct sa&)b = a; • 上面两个类型转换赋值表达式可分别理解为: • memcpy (&a,&b,sizeof (struct sa)); • memcpy (&b,&a,sizeof (struct sa)); • 赋值拷贝映射的原则是将源数据内存的状态根据目标集 • 合的长度复制给目标所占用的内存。如果sizeof(sb)大于 • sizeof(sa),那么转换 b = (sb&)a将导致对集合数据a的越 • 界,反之如a = (sa&)b则对长的源数据b前面的sizeof(sa)个 • 元素进行了复制。引用形式类型转换表示了集合数据间的直 • 接映射,当然也可以对简单变量进行引用的类型转换。

  34. [例]结构变量的类型转换与复制 • #include <stdio.h> • typedef struct sa { int m; } A; • typedef struct sb { int x; int y; } B; • void show (B& b) { printf ("b={%d,%d}\t",b.x,b.y); } • void main() • { A a={1}; B b={3,4}; • (struct sa&)b=a; show(b); • a.m=8; b=(B &)a; show(b); • } • //程序输出:b={1,4} b={8, 6684216}

  35. 十一、位域或位字段 • 位字段(bit field)是特殊之至的数据结构,该结构将整 • 型变量占有的内存按位进行细分,细分的位数可以大小不 • 等,具体的bit位置通过若干成员来索引,成员的数据类型通 • 过char , int,short ,unsigned short,long等整型或梅举类界 • 定。 • 其中b1,b2,bn表示成员,count1, count2, countn表示 • 相应成员占有的bit位的个数。

  36. 方括号包括的项表示可以省略,格式如下: • struct bit_t • { int b1 : count1; • int [b2] : count2; • [int : 0;] • ... • int bn : countn; • } t; • b2不在时表示无名位域,无名位域成员的位长度用于 • 填充位域的特定位。 • 因其无名对应的bit位不可索引。

  37. 具体地考虑一个bitDate的结构: • struct bitDate • { unsigned int year: 9; • unsigned int month:4 ; • unsigned int day: 5 ; • unsigned int week: 3 ; • } d;

  38. 如上类型声明语句建立了奇怪的位域结构,结构名为如上类型声明语句建立了奇怪的位域结构,结构名为 • bitDate,同时定义该结构的实例d。这个结构实例d占有的 • 内存大小sizeof (d)=sizeof (bitDate)=4。 • 一般地sizeof (bit_t)并不简单是其中位域成员的位数之 • 和除8,而是同时满足每一单独的成员占有的位数连续地局 • 限于字节单元中。 • 一般不将一个位域成员从一个字节单元的中间跨字节边 • 界分布。

  39. 如下代码说明位域成员如何定位于字节的起始边界:如下代码说明位域成员如何定位于字节的起始边界: • struct c{ char a:8; char b:3; char e:5; char d:7; } e; • printf ("%d,%d\n",sizeof (c),sizeof (e)); • //输出3,3 • struct r { char b:3; char a:8; char e:5; char d:7; } s[6]; • printf ("%d,%d\n",sizeof (r), sizeof (s)); • //输出4,24

  40. 内存的寻址以字节为基本单元。 • C/C++规定不取位域成员的长度也不取位域成员地址 • 即sizeof(d.year)和& d.year是不允许的。 • 成员名称year占有9个bit位,其合理的取值范围为 • 0~511。位域成员的性质类似于由其后位长度界定的整数, • 保证位域成员不要超出其各自的范围,同其它整数一样可出 • 现在算术表达式中,因此可以将 d.year等当作一个微小的整 • 数看待,作为左值或右值。 • 如: d.year=52; d.month=11; • d.week=3; d.day=18; • int k=d.day;

  41. 11 week: 3 day: 5 month:4 year: 9 • 31 23 15 7 0 • 位域各成员的细节在字节单元上的分布取决于机器的具 • 体实现,依赖于这些情况的程序是不可移植的。 • 有的编译器位域成员的分布是从左往右,有的机器上则 • 相反。微软对位域成员的排列顺序是从低到高如:

  42. 请打开 “第13章(1).ppt”

More Related