我花了一个上午,看了一些资料,总算把这个问题搞明白了。下面我以一些例子说明结构体成员变量的对齐问题。
对于
struct s1
{
char a;
long int d;
double c;
};
这个结构体的大小是16。编译器默认的一般是8字节对齐。a的大小是1,它就按1字节对齐(因为比指定的8 小),存诸在0偏移的地方;b大小为4,它就按4字节对齐(因为比指定的8小),存在偏移4——7的位置,c大小为8,存在8——15的位置。这样3个成员共占用了16个字节。由于该结构最大成员c大小为8,所以结构按8字节对齐,16按8园整还是16,因此sizeof s1 = 16.
而对于
struct s2
{
char a;
long int d;
double c;
char e;
};
这个结构体的大小是24。前3个成员和上面一样存诸,d在4——7位置,c在8——15位置,但e按1字节对齐,存在偏移位置16,这样4个成员占用了17个字节。由于该结构的最大的数据成员c的大小是8个字节,所以17对8园整得24。
当然你可以使用#pragma指令指定编译器按4字节对齐。即
#pragma pack(4) // 这里也可以是#pragma pack(push,4)
struct s1
{
char a;
long int d;
double c;
};
struct s2
{
char a;
long int d;
double c;
char e;
};
这时s1的大小还是16,而s2的大小变为20。我们来分析一下,对s1来说,按4字节对齐和按8字节对齐个数据成员的存诸位置是一样的,只不过是最后一部园整时,16对4园整还是16。对s2就不一样了,a的大小为1(比指定的4字节对齐要小),按1字节对齐,存诸在0位置,d的大小是4(大于等于指定的4字节),按4字节对齐,存诸在4——7位置,c的大小是8(大于指定的4字节),按4字节对齐,这样存诸在 8——15,e的大小是1,存储在位置16,这样整个结构体的长度是17,17对4园整,得20。你也看到并不是指定编译器按4字节对齐就按4字节对齐的。比如下面的结构体:
#pragma pack(4)
struct TestStruct2
{
char m1[11];
short m2;
};
你知道它的大小吗?是14。因为m1按1字节对齐,存诸在0——11位置,m2按2字节对齐,存诸在12——13位置。结构体占用13个字节,因为结构体内最大的成员的数据类型是short,大小为2,比指定的对齐字节4小,所以13对2园整,得14。综的说来就是结构体成员的对齐是用成员本身的大小和 #pragma pack(push,n)中的n中较小的数对齐,例如如果成员大小为2,而你指定的对齐方式是4,则该成员按2对齐;结构本身的对其是用结构中最大成员的大小和#pragma pack(push,n)中的n较小的数对齐,即最后的园整,例如如果结构中最大成员大小8,而你指定对齐是16,则结构本身按8对齐。
下面给出不同位数编译器下的基本数据类型所占的字节数:
16位编译器
char :1个字节
char*(即指针变量): 2个字节
short int : 2个字节
int: 2个字节
unsigned int : 2个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
32位编译器
char :1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
64位编译器
char :1个字节
char*(即指针变量): 8个字节
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节
long long: 8个字节
unsigned long: 8个字节