如何覆盖 C 编译器将结构中的字大小变量与字边界对齐
How to override C compiler aligning word-sized variable in struct to word boundary
我有一个指定的结构如下
- 成员1,16位
- 成员2,32位
- 成员3,32位
我将从文件中读取。我想直接从文件中读取到结构中。
问题是 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);
(*) 它几乎是可移植的,因为它假设没有字节顺序问题,根据您的需要可以正确或不正确。如果您在一台机器或一种架构上,那很好,但是如果您在大端机器上编写结构并在小端机器上读取它,就会发生不好的事情...
我有一个指定的结构如下
- 成员1,16位
- 成员2,32位
- 成员3,32位
我将从文件中读取。我想直接从文件中读取到结构中。
问题是 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);
(*) 它几乎是可移植的,因为它假设没有字节顺序问题,根据您的需要可以正确或不正确。如果您在一台机器或一种架构上,那很好,但是如果您在大端机器上编写结构并在小端机器上读取它,就会发生不好的事情...