虽然内存是以自己为单位的,但大部分计算机CPU在处理内存时,并不会直接以字节为单位。处理为了提高读取速度和减少出错几率,通常会以2字节、4字节、8字节、16字节甚至32字节为单位来存取内存,我们将上述这些存取单位称为内存存取粒度。关于更详细的内存对齐原理,这里不做详细赘述,可以参考这篇文章内存对齐。
有效对齐值
结构体中最大字节数成员的长度和对齐模数中较小的那个值。具体参照如下表达式
有效对齐值 = min{ 结构体重最大成员长度, 对齐模数 }
对齐模数:每个特定平台编译器都有自己默认的对齐系数(也称对齐模数),比如GCC中可默认对齐模数是4(#pragma pack(4)),用户也可以通过预编译指令#pragma pack(x)来指定对齐模数,x可以是1、2、4、8、16等来指定这一对齐模数
内存对齐规则
内部对齐(偏移量对齐):结构体第一个成员的偏移量(offset)为0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个值的整数倍,如有需要编译器会在成员之间加上填充字节
偏移量 offset = min { 当前成员长度, 有效对齐值 } 的整数倍
结构体第一个成员的偏移量offset为0,以后的每一个成员相对于结构体首地址的offset都是该成员大小与有效对齐值中较小的那个
外部对齐(结构体整体对齐):结构体的总大小为 有效对齐值 的整数倍,如有需要编译器会在最末一个成员之后加上填充字节
结构体的总大小 = 有效对齐值 的整数倍
实际案例
以下下案例结果统一测试方式
int main(int argc, const char * argv[]) {
NSLog(@"%lu", sizeof(s));
return 0;
}
案例具体分析
// 默认对齐模数:4
struct {
//4 offset = 0,占4位 [count = 4]
int x;
//1 偏移1的倍数,前4个字节(0-1-2-3)被占用,偏移1的倍数从4开始站一位 offset = 4 [count = 5]
char y;
} s;
// 4+1 + (3) = 8
// 默认对齐模数:4
struct {
// 4 offset = 0,占4位 [count = 4]
int i;
// 1 偏移1的倍数,前4个字节(0-1-2-3)被占用,偏移1的倍数从4开始站一位 offset = 4 [count = 5]
char c1;
// 1 偏移1的倍数,前5个字节(0-1-2-3)+(4)被占用,偏移1的倍数从4开始站一位 offset = 5 [count = 6]
char c2;
} s;
// 4+1+1 + (2) = 8
// 默认对齐模数:4
struct {
//1 offset = 0,占1位 [count = 1]
char c1;
//4 偏移4的倍数,前1个字节(0)被占用,偏移4的倍数前边填充(1-2-3),从4开始站一位 offset = 4 [count = 8]
int i;
//1 偏移1的倍数,前8个字节(0-1-2-3)+(4-5-6-7)被占用,偏移1的倍数 offset = 8 [count = 9]
char c2;
} s;
// 1+(3)+4+1 + (3) = 12
// 有效对齐值:4
struct {
char c1; // 1 offset = 0,占1位 [count = 1]
char c2; // 1 offset = 1,占1位 [count = 2]
int i; // 4 offset = 4,占1位 [count = 8]
} s;
// 1+ 1 + (2) + 4 = 8
参考文献