结构体内存对齐计算

大耗子 2020年03月12日 227次浏览

文章链接:https://codemouse.online/archives/2020-03-12223953

结构体内存对齐

  • 元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,每个元素放置到内存中时,它都会认为内存是按照自己的大小来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始。

使用场景

  1. 通常在一个结构体中,嵌套了别的结构体的时候。
    例如:在linux中默认4字节对齐,如果结构体中,第一个结构体变量14字节,第二个20字节,第三个8字节,那么第一和第二个之间就会出现两个字节的空挡,直接内存dump的时候就会出问题,而且问题很隐蔽。答案就不是42了,是(14+2+20+8=44)
  2. 做协议的时候。
  3. 要做内存dump的时候。

内存对齐的原因

  1. 平台问题(移植问题):不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地质处取某些特定类型的数据,否则跑出硬件异常。

  2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存只需要访问一次。

总结:拿空间换时间

获取结构体中某一元素的位置

使用offsetof宏来判断结构体中成员的偏移地址,在stddef.h中定义

 #define offsetof(type,menber) (size_t)&(((type*)0)->member)
 // 由于从0处算,那么获取到的结构体元素地址也就是他所在结构体的偏移值

设置默认对齐数

#pragma pack(value)   //value只能是1,2,4,8,16
#pragma pack()    //设置为默认对齐数

结构体内存对齐规则

  1. 起始情况:第一个成员在与结构体变量偏移量为0的地址处。
  2. 中间情况:成员变量的位置必定是编译器默认的一个对齐数 与 该成员大小的较小值的整数倍。
  3. 结尾情况:结构体的总大小为结构体最宽基本类型成员大小的整数倍。
  4. 默认情况:VS中默认对齐数的值为8, Linux中的默认值为4。

举例

环境:32位机
默认char一字节,short二字节,int,float四字节,double八字节

struct A{
	char a;
	int b;
	float c;
	double d;
	int e;
};

解析:
对齐数为4:
a为首个成员,从0出开始,
b大于等于对齐数,要在新的内存单位开始,也就是4处,
cde同理.大小为24刚好为默认对齐数的整数倍,所以最后大小为24.

QQ截图20200312222207

对齐数为8:
a为首个成员,从0出开始,
b小于默认对齐数,自身大小作为对齐数,则存放在4处,
c同理,则存放在8处,
d大小等于对齐数,存放在8的整数倍上,所以移动到了16处存放,
e小于对齐数,自身大小作为对齐数,则存放在16+8=24处,那么总大小为28,
但由于结构体大小必须为默认对齐数的整数倍,所以总大小为32.

QQ截图20200312221655
添加上结构体嵌套,这次默认对齐数为8

 struct A{
	char a;
	int b;
	float c;
	double d;
	int e;
};
struct B{
	char a;
	short b;
	struct A c;
	struct A d[2];
	struct A* e;
};
  • 解析:
    a为首个成员,从0出开始,
    b小于默认对齐数,自身大小作为对齐数,则存放在2处,
    c大小大于对齐数,存放在对齐数的整数倍上,所以移动到了8处存放,
    d与c同理,则存放在8+32=40处,
    e存放在40+2*32=104处,
    总大小为108,
    但由于结构体大小必须为默认对齐数的整数倍,所以总大小为112.

验证程序

#pragma pack(8)
#include <stdio.h>
#include <stddef.h>
struct A{
	char a;
	int b;
	float c;
	double d;
	int e;
};
struct B{
	char a;
	short b;
	struct A c;
	struct A d[2];
	struct A* e;
};
void main()
{
	struct B a = { 0 };
	printf("%d \n", offsetof(struct B, a));
	printf("%d \n", offsetof(struct B, b));
	printf("%d \n", offsetof(struct B, c));
	printf("%d \n", offsetof(struct B, d));
	printf("%d \n", offsetof(struct B, e));
	printf("sum=%d \n",sizeof(a));
}


QQ截图20200312223801