C语言中的字节对齐

本文详细介绍了内存字节对齐的概念,解释了为何对齐能提高数据访问速度,尤其是在某些CPU架构中。内容涉及Cortex-M3处理器对非对齐访问的支持,以及字节对齐的目的和实施方式,包括C语言中如何通过#pragmapack指令进行字节对齐控制。此外,还讨论了字节对齐对内存使用和效率的影响,以及在不同场景下如何权衡是否进行对齐。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 编辑中……

        一般作为内存的存储介质都是能以字节为单位去访问的,尽管理论上CPU可以从内存的任意起始地址读取数据,但是对于一些CPU架构,从对齐的内存地址读取数据可能比从不对齐的内存地址读取数据要快得多(顺带一提,ARM的Cortex-M系列内核在Cortex-M3出现之前都不支持非对齐访问)。因此,为了提高数据访问速度,在一些情况下,有必要进行字节对齐,即使CPU本身可以从任意内存地址读取数据。但是,请注意,字节对齐并不总是必要的,并且还可能会增加内存使用量。因此,在决定是否进行字节对齐时,应考虑系统的具体情况,并在必要时才进行字节对齐。

Cortex-M3支持在单一的访问中使用非(地址)对齐的传送,数据的访问无需对齐。在以前,ARM处理器只允许对齐的数据传送。这种对齐是说:以字位单位的传送,其地址的最低两位必须是0;以半字位单位的传送,其地址的LSB必须是0;以字节为单位的传送则无所谓对不对齐。如果使用0x1001、0x1002或0x1003这样的地址做字传送,在以前的ARM处理器中会触发一个数据流产异常。

        字节对齐是指将数据存储在内存中时,数据的起始地址与某个值(称为对齐值)的倍数对齐。对齐值通常是2的幂次方,例如1、2、4、8等。例如,如果采用4字节对齐,则将数据存储到内存中时分配到的地址总是4的倍数。值得注意的是,非常不建议使用2的幂次方以外的对齐值,因为C语言中的数据类型占据的字节大小都是2的幂次方,如char、int,使用3字节等非2的幂次方对齐值的话,内存碎片将无法避免。

        字节对齐的目的是为了提高存储器访问的效率,因为处理器在访问内存时通常会以固定大小的字(如 4 字节或 8 字节)为单位进行读写。如果内存地址不是字的边界,处理器就必须进行额外的操作来确定所需的内存位置。

         例如,在一个 32 位系统中,通常情况下 CPU 会从内存中一次性取出一个字(word)的数据,一个字的大小通常为 32 位(也有一些32位系统的字的大小为 64位或16 位,64位相较于16位常见得多)。这种情况下,系统一般

+采用的是4字节对齐。当 CPU 访问一个 char 类型的数据时,它会从内存中取出一个字的数据,然后将其中不需要的24位视为无效数据。显然,对于char类型这种一字节大小的数据,不管它的地址是有没有进行字节对齐,CPU都只用进行一次读取操作,就能得到它的值。那么如果要读取的数据大小为4字节呢?假设在这个32位系统中,long类型数据占据4个字节,现在我们要读取一个long类型的数据,且它的地址没有进行字节对齐,为0x03,那么CPU要想读取数据就必须从地址0x00处开始读取四个字节,取得0x03处的这个long型数据的低字节(小端存储),从0x04开始读取四个字节,取得这个long型数据的高字节,要进行两次读取操作。

        字节对齐是一种软件层面上的操作,我们编写的C语言程序,并不能直接被硬件平台识别,要先由编译器将代码编译成汇编代码,然后再使用汇编器将汇编代码转换成机器代码,机器代码才是硬件能够识别并执行的代码。因此,字节对齐是通过软件层面的编译器和汇编器实现的,通过指定程序执行时数据在内存中的起始地址来实现字节对齐,我们可以在C程序中使用相应的指令或在编译时更改编译器的编译选项来更改字节对齐的方式。但是,请注意,在编译器和汇编器将代码转换成机器代码时,可能会考虑硬件的限制,并调整字节对齐方式以满足硬件的要求。

        在 C 语言中,可以使用 #pragma pack 指令来控制结构体、数组或其他内存块的字节对齐方式。例如,可以使用以下语句来将结构体的字节对齐方式调整为 4 字节对齐:

#pragma pack(4) //从这之后定义的结构体采用4字节对齐
struct mystruct {
   int a;    //地址偏移:0
   char b;   //地址偏移:4
   short c;  //地址偏移:8
}; //这个结构体大小为10字节(假设这个系统的short为2字节,int为4字节)
#pragma pack()  //从这之后恢复默认的对齐方式

在这个例子中,mystruct 结构体的字节对齐方式被调整为 4 字节对齐。这意味着 mystruct 结构体的起始地址必须是 4 的倍数并且结构体中的所有字段都必须相对于起始地址对齐到 4 的倍数上

对于数组:

#pragma pack(4)
char a[3];
#pragma pack()

即使采用4字节对齐方式,数组 a 中的每个元素仍然会占用 1 个字节的内存空间,但是数组 a 的起始地址将调整为 4 的倍数。这叶符合数组说一片连续的内存空间的定义。因此,在4字节对齐下,数组 a 的内存分布情况可能会如下所示:

内存地址内存值
0x0004a[0]
0x0005a[1]
0x0006a[2]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值