如何覆盖 C 编译器将结构中的字大小变量与字边界对齐

How to override C compiler aligning word-sized variable in struct to word boundary

我有一个指定的结构如下

我将从文件中读取。我想直接从文件中读取到结构中。

问题是 C 编译器会将变量 m1、m2 和 m3 对齐到 32 位的字边界,因为我正在为以下结构声明在 ARM Cortex M3 上工作:

typedef struct
{
    uint16_t m1;
    uint32_t m2;
    uint32_t m3;
}something;

直接从文件中读取会在 m2 和 m3 中输入错误的值,并且还会读取 2 个额外的字节。

我已经四处摸索,目前正在使用以下效果很好的工具:

typedef struct
{
    uint16_t m1;
    struct
    {
        uint16_t lo;
        uint16_t hi;
    }m2;
    struct
    {
        uint16_t lo;
        uint16_t hi;
    }m3;
}something;

但是,这看起来像是一个非常肮脏的 hack。我忍不住希望有一种更简洁的方法来强制编译器将 m2 和 m3 的一半放在不同的词中,无论它可能是次优的。

我正在使用 arm-none-eabi-gcc。我知道位打包,但无法解决此优化问题。

编辑:原来我对位打包的了解不够 :D

也许#pragma pack(2)。那应该强制编译器使用 2 字节对齐

__attribute__ ((aligned (2)));

您要查找的是 packed 属性。这将强制 gcc 不对成员进行任何填充。摘自 GCC Online docs:

打包

此属性附加到枚举、结构或联合类型定义,指定用于表示该类型所需的最小内存。 为结构和联合类型指定此属性等同于在每个结构或联合成员上指定 packed 属性。在行中指定 -fshort-enums 标志等同于在所有枚举定义中指定 packed 属性。

您只能在枚举定义的右大括号之后指定此属性,而不是在 typedef 声明中,除非该声明也包含枚举的定义。

所以你想要的是这样的:

typedef struct
{
    uint16_t m1;
    uint32_t m2;
    uint32_t m3;
} __attribute__ ((packed)) something;

此外,我建议使用 compile time assertion check 来确保结构的大小确实符合您的要求。

你不能直接从文件中读取这样的结构,你永远不应该尝试这样做。未对齐可能会导致某些体系结构出现陷阱,您不应该依赖 pragma 来解决这个问题。

将文件元素读入结构元素的几乎 (*) 可移植方式,除非您确定该结构是使用与您用于读取的相同架构和对齐方式(至少兼容)编写的。

因此,对于您的用例,我建议:

fread(&something.m1, sizeof(something.m1), 1, fd);
fread(&something.m2, sizeof(something.m2), 1, fd);
fread(&something.m3, sizeof(something.m3), 1, fd);

(*) 它几乎是可移植的,因为它假设没有字节顺序问题,根据您的需要可以正确或不正确。如果您在一台机器或一种架构上,那很好,但是如果您在大端机器上编写结构并在小端机器上读取它,就会发生不好的事情...