C/C++中struct中内存对齐规则

Posted maji233

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++中struct中内存对齐规则相关的知识,希望对你有一定的参考价值。

前言

先来预热一下,下面两个简单的结构体,假设不在IDE运行测试,你能快速说出输出结果吗?不能,往下看。

struct B
   int a;
   char b;
   char c;
;

struct C
   char a;
   int b;
   char c;
;

cout<<"Size Of Struct B: "<<sizeof(struct B)<<endl;

cout<<"Size Of Struct C: "<<sizeof(struct C)<<endl;

发现一直不能快速计算出来,故怒总结本文,否则稍微改变一下结构体又会被蒙圈了,实在不能忍受这种感觉,本文力求给出最简单明了的方法。

内存对齐规则

强烈建议你先跳过下面无聊的规则,看完本文再来,如果你执意要看我也没办法…

1、没有#pragma pack宏的对齐规则

  • 1.结构体的起始存储位置必须是能够被该结构体中最大的数据类型所整除。

  • 2.每个数据成员存储的起始位置是自身大小的整数倍(比如int在32位机为4字节,则int型成员要从4的整数倍地址开始存储)。

  • 3.结构体总大小(也就是sizeof的结果),必须是该结构体成员中最大的对齐模数的整数倍。若不满足,会根据需要自动填充空缺的字节。

  • 4.结构体包含另一个结构体成员,则被包含的结构体成员要从其原始结构体内部最大对齐模数的整数倍地址开始存储。(比如struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)

  • 5.结构体包含数组成员,比如char a[3],它的对齐方式和分别写3个char是一样的,也就是说它还是按一个字节对齐。如果写:typedef char Array[3],Array这种类型的对齐方式还是按一个字节对齐,而不是按它的长度3对齐。

  • 6.结构体包含共用体成员,则该共用体成员要从其原始共用体内部最大对齐模数的整数倍地址开始存储。

2、存在#pragma pack宏的对齐

#pragma pack (n)    //编译器将按照n个字节对齐  
#pragma pack ()     //取消自定义字节对齐方式

那么对齐规则就变成下面的

  • 结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和自身对齐模数中较小的那个。

忽略了上面的对齐规则,下面来看看在C++中数据类型的内存分配大小。

数据类型字节大小
char 1
bool 1
short 2
int 4
long 4
float 4
double 8
long long 8
long double 8

攻略

确定一个结构体大小大致需要下面两个步骤即可

  • 步骤一:前面单元的大小必须是后面单元大小的整数倍,如果不是就补齐

  • 步骤二:整个结构体的大小必须是最大字节的整数倍

卧槽,对比上面凌乱的对齐规则,就上面两个步骤真可以求出结构体大小?不信?下面来看看实战分析

首先看看空结构体,C++编译器会在空类或空结构体中增加一个虚设的字节,以确保不同的对象都具有不同的地址。

struct A  
  

; 

cout<<"空结构体:"<<sizeof(struct A)<<endl;  //毫无疑问输出 1

接下来分析开头的两个结构体:(S2010+64位机器下进行调试,并将地址复制出来)

struct B
 int a;
 char b;
 char c;
;

//测试:获取内存分配地址数据
 B b = 1,‘a‘,‘b‘;
 B *pb_s = &b     // 0x003efa8c
 int *pb_a = &b.a // 0x003efa8c
 char *pb_b = &b.b// 0x003efa90
 char *pb_c = &b.c// 0x003efa91

cout<<"Struct B:"<<sizeof(struct B)<<endl; //输出为8

分析:

  • 首先确定最大单元:int a -> 4字节

  • 第一个单元是int a,是4字节,第二个是char b,1个字节,那么根据步骤一检查 4 = 1*N(第一个是第二个的整数倍成立,因此不需要补齐什么了),目前累计大小为5字节

  • 再看下一个char c,同样满足步骤一,所以放下c后共6字节

  • 最后检测步骤二是否满足,6不是4的整数倍,需要补齐,所以最后结果为8字节

再来看看第二个结构体

struct C
 char a;
 int b;
 char c;
;

 C c = ‘a‘,1,‘b‘;
 C *pc_s = &c     // 0x0047fa28
 char *pc_a = &c.a// 0x0047fa28
 int *pc_b = &c.b // 0x0047fa2c
 char *pc_c = &c.c// 0x0047fa30
	
 cout<<"Struct C:"<<sizeof(struct C)<<endl; //输出12

分析

  • 首先确定最大单元:int b - 4字节

  • 第一个单元是char a,是1字节,第二个是int b,4个字节,那么根据步骤一检查 1 != 4*N,所以第一个单元需要补齐到4字节才满足条件,目前累计大小为8字节

  • 再看下一个char c,4 = 1*N,所以放下c后共9字节

  • 最后检测步骤二是否满足,9不是4的整数倍,需要补齐,所以最后结果为12字节

好了,看到这里你可以回去看看内存的对齐规则,另外struct/class/union内存对齐原则都是一样的,但需要注意union的原理。遇到复杂情况,要做的就是冷静分析。如果有错误,欢迎指出!(不敢误人子弟啊)

以上是关于C/C++中struct中内存对齐规则的主要内容,如果未能解决你的问题,请参考以下文章

转C/C++ struct/class/union内存对齐

底层原理C/C++内存对齐详解

C语言精要总结-内存地址对齐与struct大小判断篇

C语言精要总结-内存地址对齐与struct大小判断篇

内存对齐的原则

结构体进阶知识 struct内存对齐(笔试面试常考)