打印
[其它应用]

Keil中三种手动结构体对齐方式,别用错了~

[复制链接]
4312|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
两只袜子|  楼主 | 2024-4-26 10:23 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
大家好,我是bug菌~
最近移植了一些开源组件,发现较多的语法跟编译器相关,如果没有跨平台处理,确实大大降低了程序的可移植性,其中尤为突出的就是结构体字节对齐属性的标识,通常编译器采用默认字节对齐方式,按照处理器架构的要求来决定的。
比如如下结构体在stm32中默认为4字节对齐:
  typedef struct _tag_Test1
  {
     uint8_t member1;
     uint16_t member2;
  }sTest1;

  stSize = sizeof(sTest1);

自然sizeof获得的结构体大小也是4。
然而默认对齐方式有时候并不满足我们编程的需求,比如需要降低一些内存占用,或者提高相关数据的访问效率等等,我们会手动的声明相关变量的对齐方式。
那么这里总结了下AC5编译器进行字节对齐的几种方式:
1

#pragma pack

#pragma pack 是一个编译指令,用于指定结构体、联合体和类成员的字节对齐方式。在 Keil uVision5 中,可以使用 #pragma pack 指令来设置字节对齐方式。一般我们用如下方式标识#pragma pack(n)

其中,n 是对齐系数,表示按照 n 字节对齐。常见的对齐系数包括 1、2、4、8 等。例如,若要将对齐系数设置为 4,比如:#pragma pack(4),该指令通常放置在结构体、联合体或类的定义之前,以影响其后的所有定义,这里尤其需要注意,很多时候忘记恢复字节对齐导致了一些没必要的问题。这样一来,所有在 #pragma pack 后声明的结构体、联合体或类成员都将按照指定的字节对齐方式进行排列。

那么如果我们需要取消则需要采用#pragma pack () 来取消结构体对齐。

  #pragma pack (1)
  typedef struct _tag_Test2
  {
      uint8_t member1;
      uint16_t member2;
  
  }sTest2;
  #pragma pack () // 取消结构体对齐
  stSize = sizeof(sTest2);


通过这样的定义,使得sTest2结构体整体大小只占用3个字节在,这种方式在MDK中比较常用。

当然有经验的朋友该说了,我用#pragma pack(push,n)比较多,没错,该语法也同样是可以的,比如例子:

    //#pragma pack (1)
    #pragma pack(push,1)
    typedef struct _tag_Test2
    {
        uint8_t member1;
        uint16_t member2;

    }sTest2;
    //#pragma pack () // 取消结构体对齐
    #pragma pack(pop)

    stSize = sizeof(sTest2);


既然都聊到这个份上了,该谈谈他们的差异了:

#pragma pack(n) 和 #pragma pack(push, n) 其实在功能上没太大的区别,仅仅只是在使用方面略有不同。

#pragma pack(n):

这个指令直接设置当前字节对齐系数为 n。这意味着在此指令之后声明的结构体、联合体或类成员都将按照指定的字节对齐方式进行排列。

每次使用 #pragma pack(n) 时,都会覆盖之前的对齐设置,因此它可能会影响后续的代码。

没有保存当前的对齐方式,因此在使用完之后,如果需要还原到先前的对齐方式,就需要手动重新设置。



#pragma pack(push, n):

这个指令其实也是设置当前字节对齐系数为 n。但是它还有一个功能,就是将当前的对齐方式保存到编译器的栈中。

这意味着,使用 #pragma pack(push, n) 后,可以在代码的后续部分使用 #pragma pack(pop) 来恢复之前保存的对齐方式,而不会受到之后代码中 #pragma pack 指令的影响。

因此,#pragma pack(push, n) 更灵活,可以避免在代码的后续部分不小心修改了对齐方式而导致错误。



2

__attribute__((__packed__))

__attribute__((__packed__)) 是 GCC 和一些兼容 GCC 的编译器(如 Clang)提供的一个特性,用于指示编译器以紧凑的方式存储结构体或类,即取消对齐。

使用案例如下:

    typedef  struct __attribute__ ((__packed__)) _tag_Test3
    {
        uint8_t member1;
        uint16_t member2;

    }sTest3;

    stSize = sizeof(sTest3);


那么最终结构体的大小也将是3个字节。

3

干脆的__packed

相信有经验的各位都比较喜欢使用这个属性吧:

    typedef __packed struct _tag_Test4
    {
        uint8_t member1;
        uint16_t member2;

    }sTest4;    ;

    stSize = sizeof(sTest4);


__packed 是一种特性,指示编译器取消对其成员的自然对齐。它的作用类似于 __attribute__((__packed__)),但是它更加与平台无关,因为它是一种更通用的约定,不依赖于特定编译器。

虽然说__packed 是一种常见的约定,但它并非标准 C 语言的一部分,因此在不同的编译器和平台上可能具有不同的行为,使用时应谨慎考虑平台兼容性和性能问题。

使用特权

评论回复
沙发
tpgf| | 2024-5-6 17:16 | 只看该作者
在keil中可以设置为自动对齐的方式吗

使用特权

评论回复
板凳
xiaoqizi| | 2024-5-6 20:22 | 只看该作者
不同的方法是否和不同的编译工具挂钩呢

使用特权

评论回复
地板
wowu| | 2024-5-6 20:53 | 只看该作者
第三种对齐方式在所有版本的c语言中都是支持的吗

使用特权

评论回复
5
weifeng90| | 2024-5-6 23:34 | 只看该作者
学习了,一直没有关注过。

使用特权

评论回复
6
木木guainv| | 2024-5-7 09:39 | 只看该作者
我不是 很明白 调用这个函数之后从纸面上看就对齐了吗

使用特权

评论回复
7
晓伍| | 2024-5-7 23:26 | 只看该作者
即便对程序还没有进行编译 也会自动的进行对齐吗

使用特权

评论回复
8
磨砂| | 2024-5-7 23:58 | 只看该作者
如果是非标准c语言 如何才能进行兼容呢

使用特权

评论回复
9
亚瑟| | 2024-5-24 22:28 | 只看该作者
这个结构体对齐 有什么用啊

使用特权

评论回复
10
yangjiaxu| | 2024-5-27 22:10 | 只看该作者
keil的对齐可以用插件的方式,比较快,而且也可以用vscode,这个插件更多,更好用

使用特权

评论回复
11
Henryko| | 2024-6-27 17:06 | 只看该作者
这个对齐方式在哪个地方可以设置啊

使用特权

评论回复
12
狄克爱老虎油| | 2024-7-22 14:44 | 只看该作者
修改完字节对齐后如何恢复默认啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2034

主题

7335

帖子

10

粉丝