存在内存对齐的原因
1、平台原因(移植原因):不是所有的硬件都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取得特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能的在自然边界上对齐,原因是为了访问未对齐的内存,处理器需要作两次内存访问,若内存对齐的话则仅需要访问一次。
举个例子
typedef struct Test
{
char a; (char本身占1个字节)因为内存对齐:1+7个字节
double b; (double本身占8个字节)内存对齐后还是8个字节
int c; (int本身占4个字节内存对齐变成4+4个字节
}Test;
整个Test结构体共占24个字节
内存对齐的依据
- 内置类型的对齐值:
- 自定义类型的对齐值:
当我们自定义了一个结构体例如上面的Test结构体,我们就需要按照内部最大的类型进行对齐,上面共有三种类型,字节最大的是double型占8个字节,所以我们找8字节对齐 - 程序的指定对齐值
我们可以利用#pragma pack(4)来对结构体进行指定,制定一个对齐值,结构体内部的元素按照给定的对齐值进行对齐,不够4字节的内存对齐成4字节,超过4字节的保留自己的字节,例如上面的Test结构体就会变成16字节【(1+3)+8+4】 - 程序的有效对齐值
当我们对结构体定义了一个指定的对齐值,但是结构体本身最大类型的对齐值小于指定对齐值,我们将会按照两者之间较小的那个来进行内存对齐,这就是有效对齐值
例如:#pragma pack(8)
typedef struct Test
{
char a; 1
int b; 4
char c; 1
}Test ;
这里程序的指定对齐值是8,而结构体自定义类型的对齐值是4,8大于4,则这个结构体的有效对齐值为4,故这个结构体占12个字节
结构体内部元素顺序对内存大小的影响
typedef struct Test 1
{
char a; 1
int b; 4
char c; 1
}Test 1;
typedef struct Test 2
{
char a; 1
char b; 1
int c; 4
}Test 2;
Test1所占的空间为12个字节【(1+3)+4+(1+3)】
Test2所占空间为8个字节【(1+1+2)+4】
所以为了节约内存空间,我们可以让占用空间较小的成员尽量在一起
在定义结构体的时候也有可能出现嵌套的情况
typedef struct Test
{
short a; 4
struct
{
int b; 4
double c; 8
char d; 1
};
int e; 4
}Test;
这个结构体所占空间的大小为40个字节【(4+4)+[(4+4)+8+(1+7)]+(4+4)】
这里是按照8字节对齐的,存在嵌套不是将嵌套的结构体当成一整个类型,还是按照原本的数据类型进行内存对齐
位域、位段
typedef struct Test 1
{
char a; 1 字节
char b; 1 字节
char c; 1 字节
char d; 1 字节
}Test 1;
typedef struct Test 2
{
char a : 1; 1 比特位
char b : 1; 1 比特位
char c : 1; 1 比特位
char d : 1; 1 比特位
}Test 2;
Test1占4个字节的空间
Test2占1个字节的空间
- 数据不能跨字节存储
char a:2;
char b:3;
这里共占2个字节,但不是一个字节存了a和b的一部分,而是第一个字节的两个比特位存了a,第二个字节的三个比特位存了b - 位域不能跨类型存储
char a:1;
int b:1;
这里一共占了8个字节,存储时不能跨类型,还是要根据之前的类型对齐值进行对齐,只是这里一共实际占用的是8个字节中的2个比特位
当给一个char类型的变量a开辟了2个比特位的空间,a赋值为3(0101),但是这里实际存储的时候a只有2个比特位的空间存3(0101)就会发生截断只存进去了1(01)
内存对齐的本质是用空间换时间,将空间全部变成相同的完整的空间,这样在存取利用时会更加便捷