本文主要是介绍C语言中结构体struct内存对齐原理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
我们从一段代码开始
#include<iostream>
using namespace std;int main(void)
{struct st1{char a;int b;short c;};struct st2{short c;char a;int b;};printf("sizeof(char) = %d\n", sizeof(char));printf("sizeof(short) = %d\n", sizeof(short));printf("sizeof(int) = %d\n", sizeof(int));printf("sizeof(st1) = %d\n", sizeof(st1));printf("sizeof(st2) = %d\n", sizeof(st2));system("pause");return 0;
}
在一般看来sizeof(st1)和sizeof(st2)都应该是7,但是结果却不是这样
结果如图
这到底是怎么回事呢?
因为内存对齐
内存对齐,正是因为内存对齐的影响,导致结果不同。
对于大多数的程序员来说,内存对齐基本上是透明的,这是编译器该干的活,编译器为程序中的每个数据单元安排在合适的位置上,从而导致了相同的变量,不同声明顺序的结构体大小的不同。 那么编译器为什么要进行内存对齐呢?程序1中结构体按常理来理解sizeof(st1)和sizeof(st2)结果都应该是7,4(int) + 2(short) + 1(char) = 7 。经过内存对齐后,结构体的空间反而增大了。
一,内存对齐的规则
假设m= min(#pragma pack()指定的数,这个数据成员的自身长度)
#pragma pack(n) 表示设置为n字节对齐。 VC6默认8字节对齐
1、 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是m的倍数。
2、 在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。必须是最小的倍数
3、结合1、2推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
4.各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。
5.各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节自动填充。
6.同时为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
二,案例分析
先拿st1进行分析,如图
再来分析st2,如图
内存对齐的主要作用是:
1、 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。具体原因稍后解释。
图一:
这是一般人心目中的内存印象,由一个个的字节组成,而CPU并不是这么看待的。
图二:
CPU是把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。块大小成为memory access granularity(粒度)
假设CPU要读取一个int型4字节大小的数据到寄存器中,分两种情况讨论:
1、数据从0字节开始
2、数据从1字节开始
再次假设内存读取粒度为4。
图三:
当该数据是从0字节开始时,很CPU只需读取内存一次即可把这4字节的数据完全读取到寄存器中。
当该数据是从1字节开始时,问题变的有些复杂,此时该int型数据不是位于内存读取边界上,这就是一类内存未对齐的数据。此时cpu的性能会下降
图四:
此时CPU先访问一次内存,读取0—3字节的数据进寄存器,并再次读取4—5字节的数据进寄存器,接着把0字节和6,7,8字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器。对一个内存未对齐的数据进行了这么多额外的操作,大大降低了CPU性能。
这还属于乐观情况了,上文提到内存对齐的作用之一为平台的移植原因,因为以上操作只有有部分CPU肯干,其他一部分CPU遇到未对齐边界就直接不做了。
图片来自:Data alignment: Straighten up and fly right
这篇关于C语言中结构体struct内存对齐原理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!