一、 字节对齐概念与规则
先介绍三个概念:自身对齐值、指定对齐值、有效对齐值。
自身对齐值:数据类型本身的对齐值,例如char类型的自身对齐值是1,short类型是2;
指定对齐值:编译器或程序员指定的对齐值,32位单片机的指定对齐值默认是4;
有效对齐值:自身对齐值和指定对齐值中较小的那个。
字节对齐有两个规则:
1、不但结构体的成员有有效对齐值,结构体本身也有对齐值,这主要是考虑结构体的数组,对于结构体或者类,要将其补齐为其有效对齐值的整数倍。结构体的有效对齐值是其最大数据成员的自身对齐值;
2、存放成员的起始地址必须是该成员有效对齐值的整数倍。
二、字节对齐规则验证
当前这段文字是对上图的详细描述,请先看上图有个大致印象再来阅读本段文字描述。上图代码中打印出了每个成员所在地址,也通过代码图下方的内存布局图形象表示出来,下面是上图内存布局文字描述:
- 变量a基地址为0xa0,自身对齐值为sizeof(short)=2字节;
- 变量b基地址为0xa2,自身对齐值为sizeof(double)=8字节,但指定对齐值为pack(4),所以0xa2不能被pack(4)整除,因此最终变量b基地址为0xa4,此时能被pack(4)整除;
- 变量c基地址为0xac,自身对齐值为sizeof(char)=1字节,因此0xac能被sizeof(char)整除;
- 变量d基地址为0xad,自身对齐值为sizeof(short)=2字节,但0xad不能被sizeof(short)整除,因此最终变量d基地址为0xae;
- 变量x基地址为0xb0,自身对齐值为sizeof(long)=8字节,指定对齐值为pack(4),pack(4)比sizeof(double)小,因此基地址0xb0能被pack(4)整除,因此x基地址为0xb0不变;
- 变量e基地址为0xb8,自身对齐值为sizeof(char)=1字节,因此0xb8能被sizeof(char)整除;
- 变量a基地址为0xbb,自身对齐值为sizeof(short)=2字节,基地址0xbb不能被sizeof(short)整除,所以往后移至0xbc,此时0xbc能被sizeof(short)整除,因此a基地址为0xbc。
下面是上面例程的完整源代码,大家可以直接上手试验试验。
#include <stdio.h>
#pragma pack(4)
typedef struct _GUID {
short a;
double b;
char c;
short d;
long x;
char e[3];
} GUID;
#pragma pack()
int main(int argc, char *argv[])
{
GUID guid[2] = {0};
printf("size = %ld\n",sizeof(GUID));
printf("&guid[0].a = %08x\n",&guid[0].a);
printf("&guid[0].b = %08x\n",&guid[0].b);
printf("&guid[0].c = %08x\n",&guid[0].c);
printf("&guid[0].d = %08x\n",&guid[0].d);
printf("&guid[0].x = %08x\n",&guid[0].x);
printf("&guid[0].e = %08x\n",&guid[0].e);
printf("&guid[1].a = %08x\n",&guid[1].a);
return 0;
}
三、字节对齐的应用
//一般情况为了兼容32/64位系统,通常设置指定对齐值为4字节。
#pragma pack (4)/*指定按4字节对齐*/
struct datab {
short a;
double b;
char c;
short d;
char e[3];
};
#pragma pack() /*取消指定对齐,恢复缺省对齐*/