内存对齐

内存

1、为什么要内存对齐 ?

在Xcode编译器下。一下2个结构体的大小是多少?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct StructOne {
char a; //1字节
double b; //8字节
int c; //4字节
short d; //2字节
} MyStruct1;

struct StructTwo {
double b; //8字节
char a; //1字节
short d; //2字节
int c; //4字节
} MyStruct2;
NSLog(@"%lu---%lu--", sizeof(MyStruct1), sizeof(MyStruct2));
上述代码打印出来的结果为

为什么相同的结构体,只是交换了变量 ab 在结构体中的顺序他们的大小就改变了呢?这就是“内存对齐”的现象

内存对齐规则

每个特定的平台上的编译器都有自己的默认“对齐系数”,程序员可以通过预编译命令 #pragma pack(n) n = 1,2,4,8,16 来改变这一系数,其中的n就是你要指定的对齐系数.

提示
Xcode 中默认为#pragma pack(8)。如果在代码执行前加一句#pragma pack(1) 时就代表不进行内存对齐,上述代码打印的结果就都为 16。

1、1数据成员对齐规则:

结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset(偏移)为0的地方,以后每个数据成员的对齐按照 #pragma pack 指定的数值和这个数据成员自身长度中,比较小的那个进行

1、2结构(或联合)的整体对齐规则:

在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照 #pragma pack 指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

例如:#pragma pack(4)
系数 = min(max(成员),n )
整体对齐系数 = min((max(int,short,char), 4) = 4

1、3结构体作为成员:
如果一个结构里有某些结构体成员,则结构体成员要从整体对齐系统的整数倍地址开始存储。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct StructOne { 
长度 对齐 偏移 区间
char a; 1 < 8 1 0 [0]
double b; 8 = 8 8 8 [8, 15]
int c; 4 < 8 4 16 [16, 19]
short d; 2 < 8 2 20 [20, 21]
} MyStruct1;

解读:
1、数据成员的对齐按照#pragma pack-8和自身长度中比较小的那个进行
-- char a 的自身长度为 1, 1 < 8, 按 1 对齐

2、第一个数据成员放在offset为0的地方
-- char a 的偏移为 1

3、整体对齐系数 = min((max(int,short,char,double), 8) = 8,
将 21 提升到 8 的倍数,则为 24,所以最终结果为 24 个字节
1
2
3
4
5
6
7
8
9
10
11
12
struct B {           长度        对齐      偏移     区间
char e[2]; 1 < 8 2 0 [0, 1]
short h; 2 < 8 2 2 [2, 3]
struct A {
//重这里开始,要考虑整体对齐系数,初始区间必须是整体对齐系数的倍数。
int a; 4 < 8 4 8 [8, 11]
double b; 8 = 8 8 16 [16, 23]
float c; 4 < 8 4 24 [24, 27]
};
// 整体对齐系数 = min((max(int,double ,float), 8) = 8
// 将内存大小由28补齐到8的整数倍32,∴ result = 32
};

###为什么要内存对齐

之所以要内存对齐,有2方面的原因:

平台原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台的某些特定类型的数据只能从某些特定地址开始存取。——-比如,有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么这种架构的编译器必须保证字节对齐。

性能原因:内存对齐可以提高存取效率。—–比如。有些平台每次读取都是从偶地址开始,如果一个int型(假设为32为系统)如果存放在偶地址开始的地方,那么一个读周期就可以都出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高地位进行拼凑才能得到改32位数据。

0%