结构体字节对齐

一、什么是字节对齐

现代计算机中,内存空间按照字节划分,理论上能够从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时常常在特定的内存地址访问,这就须要各类类型数据按照必定的规则在空间上排列,而不是顺序一个接一个地存放,这就是对齐。web

二、字节对齐的缘由和做用

不一样硬件平台对存储空间的处理上存在很大的不一样。某些平台对特定类型的数据只能从特定地址开始存取,而不容许其在内存中任意存放。例如Motorola 68000 处理器不容许16位的字存放在奇地址,不然会触发异常,所以在这种架构下编程必须保证字节对齐。编程

但最多见的状况是,若是不按照平台要求对数据存放进行对齐,会带来存取效率上的损失。好比32位的Intel处理器经过总线访问(包括读和写)内存数据。每一个总线周期从偶地址开始访问32位内存数据,内存数据以字节为单位存放。若是一个32位的数据没有存放在4字节整除的内存地址处,那么处理器就须要2个总线周期对其进行访问,显然访问效率降低不少。架构

所以,经过合理的内存对齐能够提升访问效率。为使CPU可以对数据进行快速访问,数据的起始地址应具备“对齐”特性。好比4字节数据的起始地址应位于4字节边界上,即起始地址可以被4整除。svg

此外,合理利用字节对齐还能够有效地节省存储空间。但要注意,在32位机中使用1字节或2字节对齐,反而会下降变量访问速度。所以须要考虑处理器类型。还应考虑编译器的类型。在VC/C++和GNU GCC中都是默认是4字节对齐。优化

三、字节对齐原则

* 一、结构体变量的首地址能被对齐数整除
 * 二、每一个成员相对首地址的offset都是对齐数的整数倍
 * 三、结构体总的大小为对齐数的整数倍
 * 四、不一样的编译器对齐数不同,gcc的对齐数是4,不是max_width

四、设置对齐方式

主要是更改C编译器的缺省字节对齐方式。code

在缺省状况下,C编译器为每个变量或是数据单元按其天然对界条件分配空间。通常地,能够经过下面的方法来改变缺省的对界条件:xml

使用伪指令#pragma pack(n):C编译器将按照n个字节对齐;
使用伪指令#pragma pack(): 取消自定义字节对齐方式。

另外,还有以下的一种方式(GCC特有语法):内存

__attribute__((aligned (n))): 让所做用的结构成员对齐在n字节天然边界上。若是结构体中有成员的长度大于n,则按照最大成员的长度来对齐。
__attribute__ ((packed)): 取消结构在编译过程当中的优化对齐,按照实际占用字节数进行对齐。

四、代码示例

/**
 * 32位OS默认按4字节对齐
 */
struct A{
	int a;
	char b;
	short c;
}AT;

struct B{
	char b;
	int a;
	short c;
}BT;

#pragma pack(2) /* 按2字节对齐 */
struct C{
	char b;
	int a;
	short c;
}CT;
#pragma pack()

#pragma pack(1) /* 按1字节对齐 ,则大小为类型长度总和*/
struct D{
	char b;
	int a;
	short c;
}DT;
#pragma pack()

#define GNUC_PACKED __attribute__((aligned(2)))  /* 按2字节对齐 */
struct KT{
     char  b;
     int   a;
     short c;
}GNUC_PACKED;

struct Tone{
	int m1;
	char m2;
	float m3;
	union Tun{
		char u1[5];
		int u2[2];
	}UT;
	double m4;
}ToneT;

void testStSize()
{
	printf("%d,%d\n",sizeof(AT),sizeof(BT));//8,12
	printf("%d,%d\n",sizeof(CT),sizeof(DT));//8,7
	printf("%d\n",sizeof(ToneT));//32
}