C/C++结构体字节对齐详解

前提:为了访问速度和效率,需要各种类型数据按照一定的规则在空间上排列;

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

为了访问未对齐的内存,处理器需要作两次内存访问;⽽对齐的内存访问仅需要
⼀次访问。

于是有了字节对齐,4个字节是一个自然对齐

为什么是4个字节?

32位机,即计算机数据总线宽度为32个,一次可以处理32位bit(即4个字节)

不论是结构体(struct)或者联合(union)在内存分布时都会有字节对齐,首先数据类型分浮点型,整型,字符类型,

我绘制下面一张表格(针对32位机)

下面是整个测试代码(vs2013运行,方便监视),我将分点论述。
 

typedef struct A
{
	int a;
	double b;
	char c;
}A;
 
 
#pragma pack(4)
typedef struct AA
{
	int a;
	double b;
	char c;
}AA;
 
#pragma pack()
 
 
typedef struct B
{
	double b;
	int a;
	char c;
}B;
typedef struct C
{
	char c;
	int a;
	char b;
 
}C;
 
 
typedef struct D
{
	char c;
	char b;
	int a;
 
}D;
 
 
int main()
{
	size_t sizeA = sizeof(A);
	size_t sizeB = sizeof(B);
	size_t sizeC = sizeof(C);
	size_t sizeD = sizeof(D);
	size_t sizeAA = sizeof(AA);
 
	system("pause");
	return 0;
}

监视结果

整个测试中,A和B形成对比,C和D形成对比。

首先, 结构体或类的自身对齐规则:应该对齐   其成员中自身对齐值最大 的那个值。

typedef struct A
{
    int a;
    double b;
    char c;
}A;


结构体A,int占4字节,在这个结构体中,应该对齐double类型,所以占据空间8个字节(后4个空置),double占8字节,char类型情况同int类型  最终  占 8+8+8=24字节。

typedef struct B
{
    double b;
    int a;
    char c;
}B;


结构体B内成员和A一样,更换顺序后,double占8字节,int应该和double对齐,提取8个字节空间,空余4个字节空间。但是char类型只占1个字节,可以利用int后空余空间,占据1字节

最终结构体B大小为8+8=16

typedef struct C
{
    char c;
    int a;
    char b;
 
}C;


结构体C ,最大为int 对齐空间为4个字节,char c 占1个字节,后面3个字节的空缺,int占4个字节, char b同char c,最终4+4+4=12字节


 

typedef struct D
{
    char c;
    char b;
    int a;
 
}D;


结构体D,是对C的优化,同例子二B对A的优化,char c和char b共同占据4个字节,int占据4个字节,4+4=8字节

对其扩展

typedef struct E
{
    char c;
    char b;
    char f;
    char g;
    int a;
 
}E;


 结构体E也只占据8个字节;

 

接下来我说一下program pack。

#pragma pack
使用指令#pragma pack (n),编译器将按照 n 个字节对齐。
使用指令#pragma pack (),编译器将取消自定义字节对齐方式。
在#pragma pack (n)和#pragma pack ()之间的代码按 n 个字节对齐。

字节对齐,我将另起炉灶,在另外一篇博客中归纳总结。

 

#pragma pack(push) //保存当前对其方式到 packing stack
#pragma pack(push,n) 等效于
#pragma pack(push)
#pragma pack(n) //n=1,2,4,8,16 保存当前对齐方式,设置按 n 字节对齐
#pragma pack(pop) //packing stack 出栈,并将对其方式设置为出栈的对齐

#pragma pack()来改变编译器的默认对齐方式。

#pragma pack(4)
typedef struct AA
{
    int a;
    double b;
    char c;
}AA;


 
#pragma pack()
结构体AA和结构体A的内容和顺序都是一样的,但是AA的大小为16个。

这是由于 #pragma pack(n)预编译时,每一次存储,成员的大小都会和n来对比,成员数据类型对齐数  小于n,则默认编译,若大于或等于(>=)n,则按n字节存储(不合理的n有时会被编译器优化,linux则会默许程序员的预编译处理)。

AA中 自身对齐数为8字节,大于4 ,int存储在4个字节大小,double本身为8字节,无法改变,存储8个字节,c占1个字节,存储pack的n字节数,占4个字节,最终 4+8+4=16字节

当然,pack可以设置为pack(1),最终只占13字节,但是我们的机器是32位机器,在保证空间的同时也要保证效率。

下面是对本文的浓缩概括。

 

一个字或双字操作数跨越了 4 字节边界,或者一个四字操作数跨越了 8 字节边界,被认为是未对齐的,从而需要两次总线周期来访问内存。

一个字起始地址是奇数但却没有跨越字边界被认为是对齐的,能够在一个总线周期中被访问。

某些操作双四字的指令需要内存操作数在自然边界上对齐。如果操作数没有对齐,这些指令将会产生一个通用保护异常。

双四字的自然边界是能够被 16 整除的地址。其他的操作双四字的指令允许未对齐的访问(不会产生通用保护异常),然而,需要额外的内存总线周期来访问内存中未对齐的数据。

缺省情况下,编译器默认将结构、栈中的成员数据进行内存对齐。

参考链接:https://blog.csdn.net/weixin_41143631/article/details/81221777

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值