结构体、联合体和位断的内存对齐问题

2/10/2017来源:ASP.NET技巧人气:356

内存对齐的原因:

1.平台原因

   不是所有硬件平台都可以访问任意地址上的任意数据;

   某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2.性能原因

  数据结构(尤其是栈)应该尽可能的在自然边界上对齐。

  原因在于在访问未对齐的内存时,处理器需要进行两次内存访问;而对齐的内存访问仅需要一次。

 

结构体(struct)内存对齐规则:

1.第一个成员在与结构体变量偏移量为0的地址处。

2.其它成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

   //对齐数=编译器默认的一个对齐数与该成员大小的一个较小值

   Vs中默认的对齐数是8

   linux中默认的对齐数是4

3结构体总大小:最大对齐数每个成员变量的除了第一个成员都有一个对对齐数)的整数倍。每个成员变量在对    齐之后,把成员大小加起来,再扩大到最大对齐数的整数倍

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有对齐数  (含嵌套结构体的对齐数)的整数倍。

 

 

联合体(union)的内存对齐规则

1.联合体也是一个结构,联和体是共享内存的。

2.所以的联合体的内部成员起始地址都是一样的,都是联合体的首地址。

3.它的对齐方式要适应所有成员。

4.该空间必须足够容纳最宽成员。

5.联合体的对齐数为最大成员的对齐数。

 

 

位断(struct)的内存对齐规则:

1.如果相邻位域字段的类型相同,且其位宽之和小于sizeof(type)的大小,则后面的字段紧邻前一个字节存储,直到  容纳不下为止;基本成员是连续存储的,若这个单元空间放不下下一个成员,则新开辟一个单元空间,这样可以节  省内存空间。

2.如果相邻位域字段的类型相同,但其位宽之和大于sizeof(type)的大小,则后面的字段将从新的单元开始,偏移量  为其类型大小的整数倍。

3.如果相邻位域字段的类型不相同,则各编译器的实现有差异,vc6采取不压缩方式,Dev-c++采取压缩。

4.如果位域字段之间穿插着非位域字段,则不进行压缩。

5.结构体的总大小为最大对齐数的整数倍。因为位断成员必须声明为int、signed int或unsigned int类型,因此结构体的  大小都是4的整数倍。

 

 

下面我们看一个代码,通过以上规则计算其大小结构体,位断,联合体的大小:

    在计算之前,我们首先需要明确的是各个数据成员的对齐模数,对齐模数和数据成员本身的长度以及PRagma pack()编译参数有关,#pragma  pack(n) 可以设定对对齐数,编译器支持往比默认对齐数小的数调。对齐数=编译器默认的一个对齐数与该成员大小的一个较小值。如果程序没有明确指出,就需要知道编译器默认的对齐模数值。

下表是Windows xp/DEV-C++Linux/GCC中基本数据类型的长度和默认对齐模数。 

 

 

char

short

int

long

float

double

long long

long double

Win-32

长度

1

2

4

4

4

8

8

8

模数

1

2

4

4

4

8

8

8

Linux-32

长度

1

2

4

4

4

8

8

12

模数

1

2

4

4

4

4

4

4

Linux-64

长度

1

2

4

8

4

8

8

16

模数

1

2

4

8

4

8

8

16

#include<stdio.h>
#include<windows.h>

#pragma pack(4)    //默认对齐数为4

struct A{
	char a1;    //1+3  char占1个字节,double要对齐到4的整数倍处,所以1+3对其到4
	double a2;  //8
	int a3;     //4
};             //结构体总大小:16   对齐数:4

union un{
	char b1;     //1
	struct A b2; //16
	int b3;      //4
};             //联合体总大小:16   对齐数为:4(最大成员的对齐数)

struct B{
	unsigned int c1 : 4;            //4
	unsigned int c2 : 31; //位域    //4
	unsigned int c3 ;    //非位域   //4
	unsigned int c4 : 1;            //4
};              //位断的总大小:16   对齐数:4
 
struct obj{
	double d1;     //8
	char d2;       //1+3
	union un d3;   //16 使1+3对齐到12,即4的整数倍
	struct B d4;   //16
	char d5;       //1+3 使1+3对齐到48,即4的整数倍
	struct C{
		struct B e1;//16
		char e2;    //1+3 使1+3对齐到20,即4的整数倍
		double e3;  //8
	};           //总大小:28   对齐数:4
	char d6;     //1+3
};               //结构体obj的总大小:80(使1+3对齐到80,即4的整数倍)  最大对齐数:4
int main()
{
	printf("%d\n", sizeof(struct obj));  //结构体obj的总大小:80
	system("pause");
	return 0;
}