全攻略有点大放厥词,哗众取宠了,其实是一些常见的问题罢了。但是自认为总结的还算全面一点吧,请大家多多赐教,我只是个初学者。
接下来主要讨论四点:
一、不涉及位域的内存对齐原则
二、涉及位域的内存对其原则
三、成员变量含有结构体的内存对齐情况
四、要求内存对齐的原因及优点
引子:
1#include <iostream>
2using namespace std;
3struct A
4{
5 char m;
6 int n;
7};
8int main ()
9{
10 A a;
11 a.m=1;a.n=2;
12 printf("sizeof(A)=%d\nsizeof(A.m)=%d\nsizeof(A.n)=%d\n",sizeof(a),sizeof(a.m),sizeof(a.n));
13 return 0;
14}
预测输出:sizeof(A)=5
sizeof(A.m)=1
sizeof(A.n)=4
实际输出:
分析:
也许该疑问了,sizeof(A.m)=1,sizeof(A.n)=4,sizeof(A)不是该1+4=5吗,怎么是8呢?
这是因为程序员眼中的内存与处理机处理内存的不一致,请看下图:
程序员通常认为内存就是一些列简单的字节数组,在C语言以及它的衍生语言中,char*被普遍认为代表一块内存区域,即使是Java也用byte[]来代表原始内存如下图所示:
处理机在对内存进行存取操作时,却不是以单个字节为单位来进行的,它通常是以2 、4、 8 、16大小的字节块来进行的。我们称处理器访问内存时一次存取的内存大小为
“内存访问粒度”,如下图所示:
可以看一下内存中的情况,确实是8个字节:
一、不涉及位域的内存对齐原则
规则如下,然后来举例说明:
1)对结构的数据成员,第一个数据成员位于偏移为0的位置,以后每个数据成员的偏移量必须是MIN(#pragma pack()指定的数,此数据成员的自身长度) 的倍数,我们称MIN(#pragma pack()指定的数,此数据成员的自身长度) 为该结构成员的对齐模数。注:程序中通常是不指定#pragma pack()它的,默认值为8,从哪里可以看到呢,看下图(发现用图说话有时是直观方便易懂……):
2)为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节(填充字节为CC,也就是int3中断),以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。3)在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照MIN(指定的#pragma pack()的数值,MAX(结构(或联合)数据成员长度))进行对齐。
这部分以这段代码为例:
1#include <iostream>
2using namespace std;
3struct A
4{
5 char c;
6 int i;
7 short s;
8};
9int main ()
10{
11 A a;
12 a.c=1;a.i=2;a.s=3;
13 printf("sizeof(A)=%d\n",sizeof(A));
14 return 0;
15}
这里,我们采用系统默认的#pragma pack(8),则
c 的对齐模数为:MIN(sizeof(char),8)=MIN(1,8)=1
i 的对齐模数为:MIN(sizeof(int),8)=MIN(4,8)=4
s 的对齐模数为:MIN(sizeof(short),8)=MIN(2,8)=2
根据规则一:
根据规则二:
当给c开辟空间以后,为i开辟空间之前,编译器先检查与开辟空间的首地址,发现为1,不是i的对齐模数的整数倍,则向后找,一直到地址4时,以4为起始,向后开辟四个字节大小的空间,而之前的1,2,3这三处则填充 CC ,如下图:
根据规则三:
现在进行结构体对齐,整个sizeof(A)=10,而MIN(8,MAX(sizeof(c),sizeof(i),sizeof(s)))=4,则sizeof(A)=MIN(4N),并且sizeof(A)>=10,故,sizeof(A)=12.再将10,11空间给填充上CC:
要知道为什么有的是CC,有的是00啊,还有,千万别看到sizeof(A)=12,就把12位置也给填上CC啊
本来打算写一篇唠叨完,但我太絮叨了,一个问题说了这么多,分开写吧……