所以,我写了一个结构体,它将被用来反序列化一个二进制数据流。为了让大家明白这一点,这里有一个精简版:
typedef struct
{
bool flag1 : 1;
bool flag2 : 1;
bool flag3 : 1;
bool flag4 : 1;
uint32_t reserved : 28;
} frame_flags_t;
typedef struct
{
/* Every frame starts with a magic value. */
uint32_t magic;
frame_flags_t flags;
uint8_t reserved_1;
/* A bunch of other things */
uint32_t crc;
} frame_t;
我的问题是,如果做到以下几点:
frame_t f;
memcpy(&f, raw_data_p, sizeof(frame_t));
我能保证f.flags.flag1
确实是第一位(在magic
成员之后,假设是一个整齐 Package 的结构体(它是这样的))吗?.flags2
将是其后的一位,等等?
据我所知,C和C++标准并不能保证这一点。
2条答案
按热度按时间pbpqsu0x1#
我能保证
f.flags.flag1
确实是第一位(在magic
成员之后,假设是一个整齐 Package 的结构体(它确实是))吗?C语言不能保证这一点。
.flags2
将是接下来的一个,等等?C语言 * 确实 * 要求分配给同一个可寻址存储单元的连续位域之间没有间隙。这可能意味着标志最终占据了同一个字节中的相邻位,但它并不 * 必须 * 意味着这一点。
据我所知,C和C++标准并不能保证这一点。
不可以。结构布局规则是 * 应用程序二进制接口 * 的特征(ABI),这是操作系统+硬件组合的一个属性。例如,对于运行在x86_64上的Linux有一个ABI,对于运行在32位x86上的Linux有另一个ABI,对于运行在这些平台上的Windows则仍然不同。GCC支持各种ABI,并且它根据目标ABI的规则来布置结构,它不能对结构布置的细节做出任何全面的保证。
例如:ABI for Linux/x86_64为https://www.intel.com/content/dam/develop/external/us/en/documents/mpx-linux64-abi.pdf,位域布局为:
位字段遵守与其他结构和联合成员相同的大小和对齐规则。
还有:
这实际上并不完全一致,但对于
frame_flags_t
,它的解释方式是:flag1
使用最低有效位flag2
使用次低有效位flag3
使用第三低有效位flag4
使用第四低有效位此外,整个
frame_t
结构在Linux/x86_64上有4字节对齐要求,并且它将使用对齐所有成员所需的最小填充进行布局。因此,在这样的计算机上,magic
成员和flags
成员之间将没有填充。x86_64是little-endian,因此,在Linux/x86_64**上,这确实会将标志位放在magic
**之后的第一个字节中。jtjikinw2#
这也适用于ARM v7,
在这个目标上,您可以安全地使用位字段,并且ABI明确指定了它们的行为。