搞不清__attribute__((aligned(n)))与__attribute__((packed))

091b8af44a3c4c43c0392278d5dd09f5.gif

正文


大家好,我是bug菌~

前段时间分享了一篇手动对齐方式设置的文章<Keil中三种手动结构体对齐方式,别用错了~>,然后有朋友私信我问到,为什么使用__attribute__((aligned(1)))进行属性声明的结构体大小不能达到__attribute__((packed))的效果,然后跟他聊了小一会,那么今天就以此文再总结总结。

1

默认对齐

其实所谓的对齐,主要是包括两个内容,数据地址的对齐与数据结构的填充,数据地址的对齐主要是方便CPU的访问,然而为了完成数据地址对齐,对于结构体数据需要插入一些无意义的数据,我们也叫数据填充。

在没有手动指定对齐方式的时候,编译器通常会进行默认自动对齐,像STM32默认采用的是自然对齐方式。在自然对齐方式下,数据类型的起始地址必须是其大小的整数倍。例如,一个四字节(32位)的整数必须从一个地址处开始,这个地址是4的倍数,都是为了提高内存访问效率。在许多存储器系统中,以4字节为单位进行访问速度更快,因为它与内存总线的宽度相匹配。这样可以减少读取和写入操作的次数,提高数据传输速率,从而提高系统性能。

2

对比

__attribute__((aligned(n))其实有很多种用法,而且其放在什么位置修饰什么内容也会产生不同的效果,最常用的就是直接修饰变量,使得变量的地址对齐到设置的对齐个数上来。

比如:

typedef  struct  _tag_Test1 
    {
        uint8_t  member1;
        uint32_t member2;
        uint8_t  member3;
    }__attribute__((aligned(16))) sTest1 ;

    Size = sizeof(sTest1);

此时aligned修饰的是结构体类型,此时在32位系统中16字节对齐,此时该结构体占用16个字节。

然后我们来看如下位置:

typedef  struct  _tag_Test1 
    {
        uint8_t  member1;
        uint32_t member2;
        uint8_t  member3;
    } sTest1 __attribute__((aligned(16)));

    static sTest1 test;

    Size = sizeof(sTest1);

此时aligned修饰的是具体的变量,并不会改变结构体的内部成员的对齐方式,仅仅只是改变结构体所定义的变量地址对齐方式。

而且使用__attribute__((aligned(n))进行对齐声明,编译器通常会将所声明的对齐方式n与编译器默认的对齐方式进行比较,取最大值来进行对齐处理,所以这就是很多朋友常提到的,__attribute__((aligned(n))在对结构体进行修饰的时候结构体大小只会大不会小。

然而__attribute__((packed))所表述的含义则不同了,它则是取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,也就是常说的采用1字节对齐的一种紧凑的对齐方式。

所以__attribute__((aligned(1))和__attribute__((packed))会得到不同的效果,__attribute__((aligned(1))通常会采用系统默认的对齐方式,而__attribute__((packed))则会采用紧凑的1字节对齐方式。

3

注意

__attribute__((packed))会让结构体以紧凑的方式进行排列,同样  #pragma pack (1)也会起到相同的效果,而__attribute__((aligned(n))) 实际上只影响紧随其后的变量或者结构体的对齐方式,而不会影响结构体内其他成员的对齐方式,当然编译器将会调整结构体的对齐方式,从而可能在结构体内部添加填充字节,以满足字节对齐的要求。

即使在结构体中某个成员使用了 __attribute__((aligned(n))),其他成员的对齐方式仍然由编译器的默认规则决定。

当然如果真的有需要对结构体内部程序进行指定地址对齐,可以使用如下操作,给内部成员对齐单独指定。

typedef  struct _tag_Test1 
    {
        uint8_t member1;
        uint16_t  __attribute__((aligned(8))) member2;

    }sTest1 ;

那么此时member2地址会落在8字节地址对齐处,member1到member2之间的多余内存会被填充,结构体大小也会发生变化。

最后

      好了,今天就跟大家分享这么多了,如果你觉得有所收获,一定记得点个~

bug菌唯一、永久、免费分享嵌入式技术知识平台~

推荐专辑  点击蓝色字体即可跳转

☞  MCU进阶专辑 3ca5a74d3ccced09309c3ac36a4ded73.gif

☞  嵌入式C语言进阶专辑 ed83ca65e94bd3a9b62a1dc8da4efb1c.gif

☞  “bug说”专辑 81b8f4aaf0204c5bff91d6550826aa51.gif

☞ 专辑|Linux应用程序编程大全

☞ 专辑|学点网络知识

☞ 专辑|手撕C语言

☞ 专辑|手撕C++语言

☞ 专辑|经验分享

☞ 专辑|电能控制技术

☞ 专辑 | 从单片机到Linux

4723c6042b93433b29f11b8ea4cde409.gif

  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值