C语言字节对齐 更多资料请访问我的博客blog.sina.com.cn/ifreecoding
太长了,发不了这么长,请下载PDF文档阅读
C语言字节对齐.pdf
(262.64 KB)
字节对齐的由来程序在运行时会将数据临时存放在内存中,芯片内核需要对这些数据进行计算,不断的读取内存以获得数据,并将计算结果写入内存。计算机体系经过若干年的发展,最终确定了以8bits作为其基本的存储单元——byte(字节),这是每个地址所对应的最小访问单元,在C语言中对应一个char型的变量。 下图为芯片内核访问内存的示意图。芯片内核通过控制总线控制内存的动作,通过地址总线告知内存地址,数据总线上出现交互的数据。
图1 访问内存示意图 假设上图是8位机的示意图,那么数据总线的宽度是8bits,由8根数据线组成,这样芯片内核与内存之间一次就可以同时交换8个bits的数据,正好是一个字节。图中右侧的每个小格子代表一个存储地址,对应一个字节。 下面通过一段C语言代码来具体看看芯片内核与内存之间的数据交互过程。 char data[2]; data[0] = 2; data[1] = data[0] + 1; 第一行代码定义了2个字节的数组data。假设data数组被编译到地址0x100,那么data[0]这个字节就被存储在地址为0x100的内存空间,data[1]这个字节就被存储在地址为0x101的内存空间。 第二行对应的硬件动作是将数据2存入到data[0]中,也就是将数据2存入到内存中的0x100地址,执行这条语句时,芯片内核对控制总线、地址总线和数据总线进行操作,控制总线上出现写信号,地址总线上出现数据0x100,数据总线上出现数据0x02。此时内存就知道需要将数据2写入到地址0x100中,完成一次写操作。 第三行先读出data[0]中的数据,芯片内核将控制总线置为读信号,将地址总线置为0x100,此时,内存就会从其内部取出0x100地址中的数据,也就是数据2,2将出现在数据总线上,此时芯片内核就会通过数据总线读取到data[0]中的数据了。接下来芯片内核计算2+1=3,需要将数字3写入到data[1]中,芯片内核将控制总线置为写信号,将地址总线置为0x101,将数据总线置为3,内存接收到这些信号后,就会将数据3存入到其内部0x101地址中,完成本次操作。 从上述介绍的过程可以看出,芯片内核与存储芯片之间每次操作可以传递1个字节的数据,如果要传递多个字节的数据就需要重复这个过程,这受限于数据总线的宽度。 计算机技术在不断的发展,在8bits数据总线之后又相继出现了16bits、32bits乃至64bits数据总线,它们分别对应于我们所谓的8位机、16位机、32位机以及64位机。对于16位机一次可以交互2个字节的数据,32位机一次可以交互4个字节的数据,64位机一次可以交互8个字节的数据,可以看出总线的带宽增加了,速度成倍提高。 以32位机为例,我们在访问0地址时,可以一次访问4个字节的数据,这4个字节的数据占用了4个内存地址,也就是说访问0地址时同时可以访问0、1、2、3这4个地址,访问4地址时可以同时访问4、5、6、7这4个地址。我们不难得出这样的结论:在地址总线上只要出一个地址,就可以连同访问这个地址及其后面的3个地址中的数据,这4个地址正好可以组成一个32bits的数据,通过访问数据总线一次即可获得,而对这个地址的要求就是:需要4字节对齐(对于64位机则需要8字节对齐)。在芯片设计时遵循了这个要求,地址总线上只需要出现0、4、8……这样4的整数倍的地址就可以同时访问连续4个字节的内存空间,这就是字节对齐的根源——是由硬件决定的!为了配合硬件的4字节对齐访问,软件的编译器链接器也对软件做了限制,需要4字节对齐访问。 有关计算机的设计五花八门,上述有关控制总线、地址总线、数据总线的介绍只是原理性的介绍,不同芯片在具体实现时会有所不同。 字节对齐规则我们在写代码时一般并不会指定变量存放在内存中的地址,这是由编译器链接器决定的,而编译器链接器则遵循了4字节对齐的原则,以32位机为例,其规则是1字节长度的变量可以被编译链接到任何地址,2字节长度类型的变量被编译链接到2的整数倍的地址,4字节长度类型的变量被编译链接到4的整数倍的地址。因此,取signed/unsigned char类型变量的地址,它可以是任意地址。取signed/unsigned short int类型变量的地址,它一定是2的整数倍。取signed/unsigned int,signed/unsigned long类型变量的地址,它一定是4的整数倍。 C语言的结构体类型由多种基本类型组成,比较利于讨论字节对齐的问题,下面我们将以结构体为例讲解字节对齐规则。以下例子除特殊说明外,均是在X86 32位CPU,VC2010环境下测试。 例1: typedef struct example1 { char a; }EXAMPLE1; 结构体EXAMPLE1比较简单,它其实就是一个char型,它的长度sizeof(EXAMPLE1)为1。 例2: typedef struct example2 { char a; short b; }EXAMPLE2; 结构体EXAMPLE2中包含了2个变量,其中char型a的长度为1,short型b的长度为2,但结构体EXAMPLE2的整体长度sizeof(EXAMPLE2)却为4,而不是1+2=3,这种现象就是字节对齐造成的。 为了方便观察结构体中变量相对结构体头的偏移地址,我们定义如下的宏: #define OFFSET(s, e) |