结构体和联合体简析

来源:互联网 发布:张信哲唱功知乎 编辑:程序博客网 时间:2024/05/17 08:13

相信大部分的程序猿都用到过结构体和联合体,今天就简单的说说结构体和联合体。

--关于结构体和联合体的定义

结构体:struct ,是由一系列相同类型或不同类型的数据构成数据的集合,也叫结构。其最主要的作用就是封装。
联合体:union,几个不同的变量存放在同一块内存区域中。也就是使用覆盖技术,几个变量互相覆盖。

 --关于结构体和联合体的长度大小计算

在结构中各成员有各自的内存空间,一个结构变量的总长度是各成员长度之和。

而在“联合”中,各成员共享一段内存空间,一个联合变量的长度等于各成员中最长的长度。
应该说明的是,这里所谓的共享不是指把多个成员同时装入一个联合变量内,
而是指该联合变量可被赋予任一成员值,但每次只能赋一种值,赋入新值则冲去旧值。

结构体中每一个成员都有自己的内存空间。因此,结构体总大小一般情况下等于各成员大小之和。

但考虑到结构体中存在内存对齐现象,用sizeof求长度需要特别注意这个现象:

sizeof应用在结构上的情况 
请看下面的结构: 
struct   MyStruct 

double   dda1; 
char   dda; 
int   type 
}; 

对结构MyStruct采用sizeof会出现什么结果呢?sizeof(MyStruct)为多少呢?也许你会这样求: 
sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13 
但是当在VC中测试上面结构的大小时,你会发现sizeof(MyStruct)为16。

你知道为什么在VC中会得出这样一个结果吗? 
其实,这是VC对变量存储的一个特殊处理。为了提高CPU的存储速度,VC对一些变量的起始地址做了“对齐”处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(vc6.0,32位系统)。 
类型 
  对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量) 

OFFSET=sizeof(variable)*N  /* ****** N=0,1,2,3...****  */

例如:
Char 
  偏移量必须为sizeof(char)即1的倍数  
int 
  偏移量必须为sizeof(int)即4的倍数  
float 
  偏移量必须为sizeof(float)即4的倍数  
double 
  偏移量必须为sizeof(double)即8的倍数  
Short 
  偏移量必须为sizeof(short)即2的倍数  

各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。 

下面用前面的例子来说明VC到底怎么样来存放结构的。 
struct   MyStruct   

double   dda1;   
char   dda; 
int   type 
}; 

为上面的结构分配空间的时候,VC根据成员变量出现的顺序和对齐方式,先为第一个成员dda1分配空间,其起始地址跟结构的起始地址相同(刚好偏移量0刚好为sizeof(double)的倍数),该成员变量占用sizeof(double)=8个字节;

接下来为第二个成员dda分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为8,是sizeof(char)的倍数,所以把dda存放在偏移量为8的地方满足对齐方式,该成员变量占用sizeof(char)=1个字节;

接下来为第三个成员type分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为9,不是sizeof(int)=4的倍数,为了满足对齐方式对偏移量的约束问题,VC自动填充3个字节(这三个字节没有放什么东西),这时下一个可以分配的地址对于结构的起始地址的偏移量为12,刚好是sizeof(int)=4的倍数,所以把type存放在偏移量为12的地方,该成员变量占用sizeof(int)=4个字节;这时整个结构的成员变量已经都分配了空间。

总的占用的空间大小为:8+1+3+4=16,刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以没有空缺的字节需要填充。所以整个结构的大小为:sizeof(MyStruct)=8+1+3+4=16,其中有3个字节是VC自动填充的,没有放任何有意义的东西。 

下面再举个例子,交换一下上面的MyStruct的成员变量的位置,使它变成下面的情况: 
struct   MyStruct   

  char   dda; 
  double   dda1;     
  int   type 
}; 
这个结构占用的空间为多大呢?在VC6.0环境下,可以得到sizeof(MyStruc)为24。结合上面提到的分配空间的一些原则,分析下VC怎么样为上面的结构分配空间的。(简单说明) 
struct   MyStruct   

    char   dda;

//偏移量为0,满足对齐方式,dda占用1个字节; 


  double   dda1;//下一个可用的地址的偏移量为1,不是sizeof(double)=8的倍数,需要补足7个字节才能使偏移量变为8(满足对齐 方式),因此VC自动填充7个字节,dda1存放在偏移量为8 的地址上,它占用8个字节。 


  int   type;//下一个可用的地址的偏移量为16,是sizeof(int)=4的倍 数,满足int的对齐方式,所以不需要VC自动填充,type存放在偏移量为16的地址上,它占用4个字节。 
};

//所有成员变量都分配了空间,空间总的大小为1+7+8+4=20,不是结构的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以需要填充4个字节,以满足结构的大小为sizeof(double)=8的倍数。 所以该结构总的大小为:sizeof(MyStruc)为1+7+8+4+4=24。其中总的有7+4=11个字节是VC自动填充的,没有放任何有意义的东西。

我们在编程过程中一定要注意结构体大小计算带来的影响,例如,在指针读取E2PROM上的数据时,当这个数据是结构体时,你指针的偏移量就要考虑结构体的内存对齐,那样指针才会正常在数据区移动,不会出现指针移到错误的位置,进而带来的读取数据出现错误,而使整个程序在运行上出现各种错误,这个在编程中必须注意!
共用体又叫联合体,每一个成员都共享内存空间。因此,共用体大小等于成员中最大的那个大小。

结构体占用的内存空间,是其元素,占空间的总和,而共用体是,元素中占用空间最大的元素的空间!所以共用体在空间开销上要小一点!联合体计算长度就比较简单,联合体的特点适合使用在赋值唯一的函数中,运用联合体更加的节约地址空间,这在嵌入式开发中经常运用到!