关于结构体的内存对齐

时间:2023-02-15 01:25:36

关于结构体的内存对齐

今日份学习“结构体的内存对齐”

前言

该篇文章主要讲解“结构体”在内存中如何存储?结构体的大小(字节)如何计算?如何更好的创建一个结构体?

OK!开始我们的表演

你认为的结构体的大小:
struct s1
{
	char c1;
  int a;
  char c2;
};
struct s2
{
	char c1;
  char c2;
  int a;
};

struct s1 a={0};,struct s2 b={0};

printf("%d\n",sizeof(a));你觉得a是多少?

printf("%d\n",sizeof(b));你觉得b是多少?

带着的答案来看它认为的答案

它认为的结构体的大小:
#include<stdio.h>
struct s1
{
	char c1;
	int a;
	char c2;
};
struct s2
{
	char c1;
	char c2;
	int a;
};
int main()
{
	struct s1 a = { 0 };
	struct s2 b = { 0 };
	printf("a=%d\n", sizeof(a));
	printf("b=%d\n", sizeof(b));
}

关于结构体的内存对齐

很明显两个结构体大小不一样,s1大小为12字节,但是s2大小为8字节

为什么两个结构体成员都是一样的,可为什么大小不一样呢?????

????探索答案????

结构体的对齐规则:

计算结构体大小时

①.第一个成员在与结构体变量偏移量为0的地址处。

②.其它成员变量要对齐到某个数字(对齐数)的整数倍的地址

③.结构体总大小为最大对齐数(每一个成员变量都有一个对齐数)的整数倍。

每个阶段的对齐数 取 其中(编译器默认的一个对齐数与该成员的大小)较小值        (vs中默认的对齐数为8)

代题解析:
struct s1
{
	char c1;
	int a;
	char c2;
};

按照上面的规则我们来计算结构体“s1”的大小

根据①,s1结构体的成员“c1”一个字节,在起始地址(偏移量为0)处计算关于结构体的内存对齐

根据②,VS默认的对齐数为8,第二个成员”a“占四个字节,且该阶段的对齐数为4(8与4取最小值为对齐数),则找4的整数倍偏移量地址开始存成员"a"的空间

关于结构体的内存对齐

为了找对齐的偏移量则需浪费三字节空间

根据②,VS默认的对齐数为8,第三个成员”c2“占一个字节,且该阶段的对齐数为1(8与1取最小值为对齐数),则找1的整数倍偏移量地址开始存成员"c2"的空间

关于结构体的内存对齐

根据③,所有成员都找到自己的对齐数的空间后,则总结该结构体总大小的空间,该阶段的对齐数取该结构体成员字节最大的作为对齐数,则4为该阶段的对齐数。

关于结构体的内存对齐

这就是s1结构体大小的计算方式

根据以上的方式试着计算一下结构体s2的大小,看看是不是8 呢?????

为什么存在内存对齐?

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

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

总的来说:结构体的内存对齐是拿空间换时间的做法。

所以我们设计数据结构的时候既要满足对齐,且还需节省空间才能做到利益最大化。

如何设计这种数据结构呢?就是让占用空间小的成员尽量集中在一起。

小扩展:

不同编译器平台的默认对齐数是不同的,在VS中默认对齐数则是8.

且也能修改它的默认对齐数

预处理指令:#pragma

用法:#pragma pack(4)//将默认对齐数改为4

#pragma pack()//取消默认对齐数

举例:

#pragma pack(4)
struct s1
{
	char c1;//该阶段对齐数位1      
  double d1;//该阶段对齐数为4
};
#pragma pack()

该结构体大小为12字节

当我们觉得默认对齐数不合适的时候可以自行设置

一般设置默认对齐数位(2),(4),(8),(16).....2的次方的对齐那启到实质性的作用(视情而定)

若设置为(1)(3)(5)(7)....的奇数并不适用

以上就是该文章的内容

拜拜~

关于结构体的内存对齐