镜片下的伪装过程如下: 1、 在A a;处设断点,Go; 2、 找到 a在内存空间中的地址为 0012ff38,如下图: 3、 查看现在0012ff38起始的内存 空间存储情况: 4、单步调试如下图(反汇编代码 内存空间分配注释):
需要内存对齐的原因: 如果你不理解和处理软件 中的对其问题,以下糟 糕的情况在你的软件中 将会越来严重: 1) 软件运行速度越来 越慢 2) 应用程序被锁定 3) 操作系统崩溃 4) 软件会因错误的结 果而逐渐被淘汰 内存对齐的优点 : 1) 实现平台无关:不 是所有的硬件平台都能 访问任意地址上的任意 数据;某些硬件平台只 能在某些地址处取某些 特定类型的数据,否则 抛出硬件异常。 2) 内存对齐以后,处 理器的读取速度将会大 大提高
可以看得出来,内存存取粒度为4的处理器能够快速的一次从对齐的地址中 读取四个字节。而从为对齐的地址中却需要翻倍的次数。 这里细说一下粒度为2从地址1开始的情况,此时,CPU先访问一次内存, 读取0-1自己的数据进寄存器,并再次读取2-3字节的数据进寄存器,接着把 字节0和字节3的数据剔除,合并1,2字节的数据进寄存器。
内存读取粒度为2时内存读取次数是粒度为1时次数的一半。而每次内存读取都需 要固定数额的开销,所以尽量减少内存读取次数将会有效的帮助处理器提高性能。 不过,请注意当从地址1开始读取时的情况。由于这个地址没有均匀的落在处理 器的内存读取边界上,处理器就需要做一些额外的开销。这样的地址就被称为 未对齐 地址(暂且这样翻译,我看到原文是斜体字,应该是专有名词,不知道是不是这样翻译 的,如果不是的话,请留言或回复说一下,谢谢了~)。因为地址1是未对齐的,当存取 粒度为2时,处理器就要做一次额外的内存读取操作,这样就会减低操作的效率。 最后呢,我们来检测一下内存读取粒度为为4时的情况:
接下来,为s2分配空间,重复第二步,非配如下(此处也要注意一下 的,因为一个s2的空间非配跨越了两个字节,而且都不是完整的占有 一个字节):
接下来为i分配内存空间便如同上一节讲的一样了,如下图: 最终结果: sizeof(A) = 8 我们看最终的内存空间分配结果:
要知道为什么有的是CC,有的是 啊 还有, 要知道为什么有的是 ,有的是00啊,还有,千万别看到 sizeof(A)=12,就把 位置也给填上 啊 位置也给填上CC啊 ,就把12位置也给填上
1) 若相邻成员变量类型相同,且其位宽之和不大于成员变量类型位 宽(在此严重强调,是 类型位宽 而不是成员变量sizeof,也不是 类型的sizeof或者其他什么)大小,则后面的字段将紧邻前一个字 段存储,直到不能容纳为止; 2) 如果相邻位域字段的类型相同,但其位宽之和不大于成员变量的类型 宽度大小,则后面的字段将从新的存储单元开始,其偏移量为其类型 大小的整数倍;
为了阐明内存对齐背后的规则,我看一个常见的小问题,并且来看一下不同的内存读取粒度对这个问题 有什么影响。这个问题很简单: 1) 从地址0读取四个字节到处理器的寄存器 2) 从地址1读取四个字节到处理器的寄存器
这符合天真的程序员所认为 的内存工作的模式:从地址0和 从地址1开始的四次内存访问是 相同的。下面来看一下处理器 的内存读取粒度为2时的情况:
2.接下来,为c2分配空间,由于c1和c2的类型都 为char,且c1和c2的位宽之和为8不大于类型 char的位宽8,根据规则1,c2的空间分配如下:
接下来,为s1分配空间,此时,有一个问题,s1的内存空间的起始地 址是从哪儿开始呢?1还是2?根据上一节介绍的我们可知,s1的对齐 模数为2,此处上一节的规则规则依然适用,结合这里的第三条,1处, 填充CC,从2处开始分配:
3)在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐, 对齐将按照MIN(指定的#pragma pack()的数值,MAX(结构(或联 合)数据成员长度))进行对齐。
2)为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址 相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成 员,反之,则在本成员和上一个成员之间填充一定的字节(填充字节为CC, 也就是int3中断),以达到整数倍的要求,也就是将预开辟空间的首地址 后移几个字节。
处理机在对内存进行存取操作时,却不是以单个字节为单位来进行的, 它通常是以2 、4、 8 、16大小的字节块来进行的。我们称处理器访问内存 时一次存取的内存大小为“内存访问粒度”,如下图所示:
规则如下,然后来举例说明: 1)对结构的数据成员,第一个数据成员位于偏 移为0的位置,以后每个数据成员的偏移量必 须是MIN(#pragma pack()指定的数,此数据 成员的自身长度) 的倍数,我们称 MIN(#pragma pack()指定的数,此数据成员 的自身长度) 为该结构成员的对齐模数。 注:程序中通常是不指定#pragma pack()它的, 默认值为8,从哪里可以看到呢,看下图(发 现用图说话有时是直观方便易懂……):
3) 如果相邻的位域字段的类型不同,不同位域字段存放在不 同的位域类型字节中;(这一条其实会根据编译器的不同 而采用不同的规则,此处以VC6.0标准来说明)
4)其实不存在4),这一条是说,在此,还要遵守不涉及位域的2),3)条规则, 也部分遵守不涉及位域的第一条规则
一、不涉及位域的内存对齐原则 二、涉及位域的内存对其原则 三、成员变量含有结构体的内存对齐情 况 四、要求内存对齐的原因及优点
sizeof(A.m)=1,sizeof(A.n)=4,sizeof(A)不是该14=5吗,怎么是8呢? 这是因为程序员眼中的内存与处理机处理内存的不一致,程序员通常认为内存 就是一些列简单的字节数组,在C语言以及它的衍生语言中,char*被普遍认为代 表一块内存区域,即使是Java也用byte[]来代表原始内存如下图所示:
|