【问题标题】:Are structs containing only chars paddingless?仅包含字符的结构是否无填充?
【发布时间】:2021-07-28 15:05:48
【问题描述】:

我有一些旧代码(目标架构 armv5tejl 和 armv7l)声明了一个如下所示的结构:

#pragma pack(push,1)
struct modbus_pkt_s
{
    uint8_t d_addr;
    uint8_t cmd;
    uint8_t payload[250];
    uint8_t payload_length;
    uint8_t pkt_length;
    modbus_erc_t erc;
};
#pragma pack(pop) 

其中 modbus_erc_t 是一个枚举:

typedef enum modbus_erc_e modbus_erc_t;

我还有一个函数可以计算 modbus 数据包的 CRC(它使用 d_addrcmdpayload),它假设前三个结构字段被打包。

我想删除 #pragma 指令,因为我遇到了未对齐的内存访问问题(导致 SIGBUS)。

考虑到它们是无符号字符,我可以安全地删除那些#pragma 指令并假设前三个字段被打包吗?

【问题讨论】:

  • 我会更改计算 CRC 的函数,使其不再依赖于要打包的那些字段。
  • 当然可以,但我很乐意避免接触该代码。
  • 试试看会发生什么。
  • 它有效。但我正在寻找一种不受架构影响且能保证工作的解决方案。
  • 该结构似乎用于通过不同设备之间的通信接口传输数据,所以不 - 不要删除打包的。而是确保接收器将数据放入正确对齐的数据结构中

标签: c struct alignment padding pragma


【解决方案1】:

C 标准没有指定结构中的填充;允许编译器出于任何原因在成员之间插入填充。编译器通常没有理由在大小为一个字节的成员或此类成员的数组之前插入填充,而 GCC 和 Clang 则不会。 (可以想象,在某些架构中对齐数组可能会带来一点好处,以使基地址更简单。这在此处不是实际问题。)

即使较早的成员不是单字节类型,您也可以通过删除围绕结构的#pragma 指令并用__attribute__((__packed__)) 标记每个较早的元素来获得您请求的布局。例如这段代码:

#include <stdio.h>


int main(void)
{
    typedef struct
    {
        char a __attribute__((__packed__));
        int  b __attribute__((__packed__));
        int  c;
    } foo;
    foo x = {0x1, 0x02030405, 0x06070809};
    unsigned char *p = (void *) &x;
    for (size_t i = 0; i < sizeof x; ++i)
        printf("%02hhx ", p[i]);
    printf("\n");
}

Apple Clang 11 显示第一个 int 已打包,但第二个正常对齐:

01 05 04 03 02 00 00 00 09 08 07 06

但是,虽然删除 pragma 指令仍会将您的早期成员打包在 GCC 和 Clang 中,但它可能会移动 erc 成员。结构被打包是有原因的,而且原因可能不仅仅是因为 CRC 可以假设早期的成员被打包。这种打包通常用于通过某个通信通道传输的结构,并且仅在通道的一侧更改结构会中断通信 - 发送方和接收方将使用不同的布局。

此外,您遇到未对齐访问错误的事实表明您的程序在使用压缩结构之外做错了事。打包结构时,编译器会生成适当的代码来访问其未对齐的成员。要出现错误,程序必须执行其他操作,例如将压缩的modbus_erc_t 成员的地址分配给未标记为压缩的modbus_erc_t *,然后尝试取消引用该指针。如果是这样,则该代码已损坏,应予以修复。

【讨论】:

  • 谢谢!实际上,该结构是为通过 modbus 传输数据而打包的,但只有前三个字段被传输,据我所知,数组成员是打包的。至于对齐问题,如何“标记打包”指针?
  • @godo:我希望你可以用modbus_erc_t __attribute__((__aligned__(1))) *p; 定义这样的指针。但是,通常最好只将未对齐的数据从结构中复制一次,就像 modbus_erc_t copy = original.erc; 一样,而不是重复使用指向结构的指针,因为后者会生成多个未对齐的访问,而复制只会生成一个。
猜你喜欢
  • 2018-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-07
  • 2015-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多