1 ANSI C规定
ANSI C规定一种结构类型的大小是它所有字段的大小以及字段之间或字段尾部的填充区大小之和。
填充区就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间。
那么结构体本身有对齐要求,ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格。
2 WIN32和Linux平台下的对齐基本规则
许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k
(通常它为4或8)的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。
Win32平台下的微软C编译器(cl.exe for 80x86)在默认情况下采用如下的对齐规则:
任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。比如对于double类型(8字节),就要求该类型数据的地址总是8的倍数,
而char类型数据(1字节)则可以从任何一个地址开始。
Linux下的GCC奉行的是另外一套规则(在资料中查得,并未验证,如错误请指正):
任何2字节大小(包括单字节吗?)的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型(比如long,double)
都以4为对齐模数。
3 vc6中对齐处理
vc6中的编译选项有 /Zp[1|2|4|8|16] ,/Zp1表示以1字节边界对齐,相应的,/Zpn表示以n字节边界对齐。
n字节边界对齐的意思是说,一个成员的地址必须安排在成员的尺寸的整数倍地址上或者是n的整数倍地址上,取它们中的最小值。
也就是:
min ( sizeof ( member ), n)
实际上,1字节边界对齐也就表示了结构成员之间没有空洞。
要使用这个选项,可以在vc6中打开工程属性页,c/c++页,选择Code Generation分类,在Struct member alignment可以选择。
/Zpn选项是应用于整个工程的,影响所有的参与编译的结构。该缺省/Zp 紧凑值为/Zp8,也就是说,缺省是8字节对齐。
要专门针对某些结构定义使用对齐选项,可以使用#pragma pack编译指令。
#pragma pack(push) //保存对齐状态
#pragma pack(1) // 1 bytes对齐
typedef struct
{
double dValue1;
char u8Value2;
int u32Value3;
} ASampleStructor;
#pragma pack(pop)//恢复对齐状态
上例中,size值为13,说明1字节对齐后,该结构总长为13字节。去掉对齐后,为16字节。
4 ARM平台中的对齐
在ARM平台的编译器中,没有提供象“#pragma pack”这么丰富的带参数对齐指令,只有一个关键字“__packed”。
__packed 限定符将所有有效类型的对齐边界设置为 1,如果一个结构没有这个限定符,默认向表数能力最强的那个数据类型对齐。
typedef __packed struct
{
double dValue1;
char u8Value2;
int u32Value3;
} ASampleStructor;
上例中,size值为13,说明1字节对齐后,该结构总长为13字节。去掉__packed对齐后,为16字节。
5 一种与对齐相关的可随时运行在VC环境或ARM(Keil)环境下的定义
我们时常会把嵌入式平台上的代码拿到PC环境中去测试,这时环境的差异将使代码移植变得困难。
下面这种结构,将会使移植变得非常轻松。
#ifdef WIN32
#define __packed //在VC环境下,将此关键字定义为空
#pragma pack(push) //保存对齐状态
#pragma pack(1) // 1 bytes对齐
#endif
typedef __packed struct
{
double dValue1;
char u8Value2;
int u32Value3;
} ASampleStructor;
#ifdef WIN32
#pragma pack(pop)//恢复对齐状态
#endif
这样,无论是ARM还是VC,都可以编译,代码的同步将非常简单。
不过呢,还有一种方法更为简单,可以使代码运行在ARM(Keil)或VC环境下。
#pragma pack(push,1)
typedef struct
{
double dValue1;
char u8Value2;
int u32Value3;
} ASampleStructor;
#pragma pack(pop)