结构体内存对齐的规则
- 结构体的第一个成员直接对齐到相对于结构体变量起始位置为0的偏移处。
- 从第二个成员开始,要对齐到某个【对齐数】的整数倍的偏移处。
- 对齐数:结构体成员自身大小和默认对齐数的较小值(vs的默认值是8)(Linux没有默认对齐数,对齐数就是结构体成员的自身大小)
- 结构体的总大小,必须是最大对齐数的整数倍。每个结构体成员都有一个对齐数,其中最大的对齐数就是最大对齐数。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
举个例子
struct tagTest1
{
short a;
char d;
long b;
long c;
};
int main()
{
struct tagTest1 stT1;
printf("%d“, sizeof(stT1)); //12
return 0;
}
short a因为是第一个成员,所以直接从结构体变量起始位置为0的偏移处开始。(short的对齐数是2)
char d是第二个成员,要对齐到char类型的整数倍的偏移处。(char的对齐数是1)
long d,c跟char一样,要对齐到自己类型的整数倍的偏移处。(long的对齐数是4)
红色块的就是浪费的空间,虽然内存不要钱,但是为啥要浪费呢?我后面会讲为啥要浪费。
所以tagTest1的大小是12字节,0-11(0也是一个字节)所以一共是12个字节,12是tagTest1里面最大对齐数的倍数。
下面是嵌套了结构体的代码
struct tagTest1
{
short a;
struct tagTest2 stT2;
char d;
long b;
long c;
};
struct tagTest2
{
long b;
short c;
char d;
long a;
};
int main()
{
struct tagTest1 stT1;
printf("%d", sizeof(stT1));
return 0;
}
short a因为是第一个成员,所以直接从结构体变量起始位置为0的偏移处开始。(short的对齐数是2)
第二个成员是结构体,对齐到他的最大对齐数的整数倍的偏移量,然后加上他自己的大小。
char d要对齐到char类型的整数倍的偏移处。(char的对齐数是1)
long d,c跟char一样,要对齐到自己类型的整数倍的偏移处。(long的对齐数是4)
所以stT1的大小是28字节,0-27,(0也算一个字节)28是stT1最大对齐数的倍数。
红色是浪费的内存空间。
如何知道某个结构体成员变量相对于结构体起始位置的偏移量呢
我们可以使用offsetof这个宏,他的第一个参数是结构体的变量名,第二参数是结构体的成员名。不懂得可以看上面那个图
struct tagTest2
{
long b;
short c;
char d;
long a;
};
struct tagTest1
{
short a;
struct tagTest2 stT2;
char d;
long b;
long c;
};
int main()
{
struct tagTest1 stT1;
printf("%d %d %d %d %d",
offsetof(struct tagTest1, a), //第一个参数是结构体的变量名,第二个是结构体的成员名。
offsetof(struct tagTest1, stT2),
offsetof(struct tagTest1, d),
offsetof(struct tagTest1, b),
offsetof(struct tagTest1, c)) ;
//运行结果是0 4 16 20 24
return 0;
}
修改默认对齐数
#pragma pack(4) //设置默认对齐数
//#pragma pack() 恢复默认对齐数
struct tagTest2
{
long b;
short c;
char d;
long a;
};
int main()
{
struct tagTest2 stT2;
printf("%d", sizeof(stT2)); //11
return 0;
}
第一个老规矩是从0偏移量开始的
因为我把默认对齐数设为1,所以没有一个空间浪费,所以这次的结构体一共是11个字节,为啥捏,因为对齐数是结构体成员自身大小和默认对齐数的较小值,而1比他们都小(除了char)。所以对齐数变成了1,这次结构体也是11字节,而不是12字节。
为啥要存在内存对齐
- 平台原因(移植原因)
大部分的参考资料都是如是说的
不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
** 2. 性能原因:**
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问。(所以会有被浪费的内存)
总体来说
结构体的内存对齐是拿空间换取时间的做法。
那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到呢
让占用空间小的成员变量集中在一起
就像这样
struct tagTest2
{
char d;
int c;
char a;
};
struct tagTest1
{
char a;
char d;
int c;
};
int main()
{
struct tagTest2 stT2;
struct tagTest1 stT1;
printf("%d %d", sizeof(stT2),sizeof(stT1)); //stT2=12 stT1=8
return 0;
}
第一个就是8字节 第二个就是12字节
xdm写的不好,如果大家觉得我有说得不对的话,欢迎大家批评指导。