【问题标题】:What is a "packed" structure in C?什么是 C 中的“打包”结构?
【发布时间】:2023-04-02 21:31:01
【问题描述】:

我正在研究一些为 Microchip C30 编译器编写的 C 代码,我经常看到定义如下的结构:

typedef struct __attribute__((__packed__)) 
{
    IP_ADDR     MyIPAddr;               // IP address
    IP_ADDR     MyMask;                 // Subnet mask
    IP_ADDR     MyGateway;              // Default Gateway
        // etc...
} APP_CONFIG;

打包是什么意思?

【问题讨论】:

    标签: c microcontroller packed


    【解决方案1】:

    定义结构时,允许编译器添加填充(没有实际数据的空格),以便成员位于更易于 CPU 访问的地址边界内。

    例如,在 32 位 CPU 上,32 位成员应该从 4 字节的倍数地址开始,以便有效地访问(读取和写入)。下面的结构定义在两个成员之间添加了一个 16 位的填充,以便第二个成员落在正确的地址边界内:

    struct S {
        int16_t member1;
        int32_t member2;
    };
    

    上述结构在32位架构下的内存结构为(~ = padding):

    +---------+---------+
    | m1 |~~~~|   m2    |
    +---------+---------+
    

    打包结构时,不会插入这些填充。编译器必须生成更多代码(运行速度较慢)来提取未对齐的数据成员,并写入它们。

    相同的结构在打包后会在内存中显示为:

    +---------+---------+
    | m1 |   m2    |~~~~
    +---------+---------+
    

    【讨论】:

    • 切向的问题,但如果它使事情变慢,为什么要创建一个打包结构?是为了减少内存占用吗?
    • 一个用例:它们经常用于定义和实现网络协议、二进制格式...您通常不希望向将要存储或发送的结构添加填充网络。
    • 考虑到您的示例,16 位 + 32 位 = 48 位 = 12 个字节,这确实是 4 个字节的倍数。为什么编译器会应用填充?
    • @Maxitj,48 位是 6 个字节,而不是 12 个。
    • @Juliano 愚蠢的我,我读这篇文章的时候真的很累 :)
    【解决方案2】:

    它指示编译器不要在struct 的成员之间添加任何填充。

    例如,请参阅this page

    【讨论】:

    • 我猜你的意思是相反的 - 打包的意思是省略任何填充而不是添加它。
    • 此答案中的链接已损坏,有修复的机会吗?
    • 详情:“成员之间不添加任何填充”更像是添加最小包装。在某些情况下,某些架构可能仍需要一些填充。 (例如,int 必须在偶数地址边界上以防止总线故障。)
    【解决方案3】:

    让我解释一下结构中填充的概念和 然后通过一个例子来打包结构。

    然后让我们看看为什么需要打包。

    填充:

    struct eg_struct
    {
               unsigned char abc;
               unsigned int  xyz;
    }
    

    当结构在 16 位架构上声明为上述结构时,变量abc 将被分配一些地址。下一个地址不分配给变量xyz,而是增加一个字节,然后下一个地址将分配给变量xyz

    最后,结构如下所示:

    struct eg_struct
    {
               unsigned char abc;
               unsigned char paddedbytes[1];
               unsigned int  xyz;
    }
    

    填充使微控制器可以轻松访问成员变量的地址。 缺点是出现了额外的不必要字节。

    包装:

    如果使用属性“packed”声明相同的结构,则不会在变量abc之后添加额外的字节。

    让我举一个需要打包的例子:

    考虑一个与 EEPROM 接口的微控制器,其中存储了一些结构。

    想象一个写入 EEPROM 的函数如下所示:

    Write_EEPROM(EEPROM address, Ram address, Byte count);
    

    现在如果不打包,多余的填充字节会占用EEPROM中的空间,这是没有用的。

    【讨论】:

    • @laurenz albe 感谢您让我的回答更形象化。
    【解决方案4】:

    _attribute__((__packed__)) 表示(很可能)“不要插入任何填充以使事情更快”,也可能意味着“不要插入任何对齐以保持对齐”。

    【讨论】:

      【解决方案5】:

      没有明确指出的一件事是打包通常是为了匹配预定义的字段结构。例如,在网络接口的低层,网络机器之间交换一系列字节。接收到数据后,需要将其映射到高级结构,以便轻松操作数据。这是通常不需要填充的时候,因此结构直接映射到字节。

      网络数据交换还涉及字节字节序问题(即几乎所有网络数据都使用大字节序格式,而不管源机器和目标机器的字节序)。

      此外,某些机器无法访问非对齐地址中的宽数据,例如,Cortex-M0 内核无法访问非 32 位对齐地址中的 32 位数据,因此必须注意在这种情况下编写网络代码。

      【讨论】:

        猜你喜欢
        • 2021-05-29
        • 1970-01-01
        • 2020-12-15
        • 2020-03-05
        • 1970-01-01
        • 2021-12-15
        • 2019-11-15
        • 2016-01-03
        • 2017-01-02
        相关资源
        最近更新 更多