字节对齐的细节和编译器实现相关,一般满足以下三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 记住:结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding).
说明:
(1)这里所说的“数据宽度”就是指其sizeof的大小.由于结构体的成员可以是复合类型,比如另外一个结构体,所以在寻找最宽基本类型成员时,应当包括复合类型成员的子成员,而不是把复合成员看成是一个整体.但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待.
(2)有一个影响sizeof的重要参量还未被提及,那便是编译器的pack指令.它是用来调整结构体对齐方式的,不同编译器名称和用法略有不同,VC6中通过#pragma pack实现,也可以直接修改/Zp编译开关.#pragma pack的基本用法为: #pragma pack( n ),n为字节对齐数,其取值为1、2、4、8、16,默认是8,如果这个值比结构体成员的sizeof值小,那么该成员的偏移量应该以此值为准.
(3)“空结构体”(不含数据成员)的大小不为0,而是1.
例1:
#include "stdio.h"
void main()
{
struct list
{
int a; //4个字节
char b[5]; //占5个+3个空字节=8字节
}s[3];
char c[5];
printf("%d\t\t\t%d\t\t\t%d\n",sizeof(int),sizeof(c),sizeof(struct list));
}
输出: 4 5 12
解释:
32位对齐,也就是4个字节
struct list
{
int a; /4个字节
char b[5]; /占8字节
}s[3];
例2:
#include "stdio.h"
void main()
{
struct time
{
int year; //4
short month;//2
short day;//2
short hour;//2
short minute;//2
short second;//2
};
struct Battery
{
short list; //2
int Integer; //4
int Deci; //4
struct time date; //当前时间
};
printf("%d\t\t\t%d\n",sizeof(time),sizeof(Battery));
}
输出:
16 28
解释:
在struct time中,字节数总和是14,根据上述第三个准测实际大小是最大成员的整数倍,所以4*4=16>14。所以输出字节是14+2个填充字节。struct Battery中struct time date是根据说明1中要求,把struct time date看成整体16字节,16+2+4+4=26,在计算struct Battery 大小时,要看struct time date中的子成员与struct Battery 的3个子成员中最大字节数的整数倍。所以是4*7=28>26。所以struct Battery 的大小为28(最后2位为填充字节)。
例3:
#include "stdio.h"
void main()
{
struct time
{
int year; //4
short month;//2
short day;//2
short hour;//2
short minute;//2
short second;//2
};
#pragma pack(1) //表示1字节对齐
struct Battery //注意 字节对齐问题 4个字节(32位)内存同一分配
{
short list; //2
int Integer; //4
int Deci; //4
struct time date; //当前存储时间
};
#pragma pack() //取消对齐
printf("%d\t\t\t%d\n",sizeof(time),sizeof(Battery));
}
输出:
16 26
解释:
#pragma pack(1) //表示1字节对齐---根据上述说明2。当取1字节对齐后无需加入填充字节,所以最后struct Battery 的大小为16+2+4+4=26.