字节对齐问题
为了能使CPU对变量进行高效快速的访问,变量的起始地址应该具有某些特性,
即所谓的“对齐”。例如对于4字节的int类型变量,其起始地址应位于4字节边界上,
即起始地址能够被4整除。变量的对齐规则如下(32位系统):
char
在字节边界上对齐
short (16-bit)
在双字节边界上对齐
int and long (32-bit)
在4字节边界上对齐
float
在4字节边界上对齐
double
在8字节边界上对齐
字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成
员之间加上填充字节;例如上面第二个结构体变量的地址空间。
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员
之后加上填充字节。
例如:
struct s1
{
char a;
short b;
char c;
};
在上述结构体中,size最大的是short,其长度为2字节,因而结构体中的char成员a、c都以2 为单位对齐,sizeof(s1)的结果等于6;
a b c
s1的内存布局 1* 11 1* (*表示填充的字节,下同)
若改为
struct s1
{
char a;
int b;
char c;
};
其结果显然为12。
a b c
s1的内存布局 1*** 1111 1***
2.又如:
struct S1{
char a;
long b;
};
struct S2 {
char c;
struct S1 d;
long long e;
};
sizeof(S2)结果为24;
再看两个结构体成员比较复杂的例子:
Typedef struct student
{
Char name[10];
Long sno;
Char sex;
Float score [4];
} STU;
sizeof(STU)=?
STU的内存布局 name sno sex score
1111111111** 1111 1*** 1111111111111111
sizeof(STU)=10+2+4+1+3+16
如果我们把STU中的成员改一下顺序:
Typedef struct student
{
Char name[10];
Char sex;
Long sno;
Float score [4];
} STU;
STU的内存布局 name sex sno score
1111111111 1* 1111 1111111111111111
sizeof(STU)=10+2+4+16
注意:
1) 对于空结构体,sizeof == 1;因为必须保证结构体的每一个实例在内存中都
有独一无二的地址。
2) 结构体的静态成员不对结构体的大小产生影响,因为静态变量的存储位置与
结构体的实例地址无关。
例如:
struct {static int I;} T;
sizeof(T) == 1;
struct {char a; static int I;} T1;
sizeof(T1) == 1;
这里有个陷阱,对于结构体中的结构体成员,不要认为它的对齐方式就是他的大小,看下面的例子:
struct s1 { char a[8]; }; struct s2 { double d; }; struct s3 { s1 s; char a; }; struct s4 { s2 s; char a; }; cout << sizeof(s1) << endl; // 8 cout << sizeof(s2) << endl; // 8 cout << sizeof(s3) << endl; // 9 cout << sizeof(s4) << endl; // 16
s1和s2大小虽然都是8,但是s1的对齐方式是1,s2是8(double),所以在s3和s4中才有这样的差异。所以,在自己定义结构体的时候,如果空间紧张的话,最好考虑对齐因素来排列结构体里的元素。