我已经在这一段时间了,这真让我感到困惑。这是一个非常精简的代码片段,可以重现这个问题:
uint8_t dataz[] = { 1, 2, 3, 4, 5, 6 };
struct mystruct {
uint8_t dummy1[1];
uint16_t very_important_data;
uint8_t dummy2[3];
} *mystruct = (void *) dataz;
printf("%x\n", mystruct -> very_important_data);
您期望输出的是什么?我会说x302,但不是。它给了我x403。与使用此结构相同:
struct mystruct {
uint8_t dummy1[2];
uint16_t very_important_data;
uint8_t dummy2[2];
} *mystruct = (void *) dataz;
你会如何解释?
答案 0 :(得分:5)
包装。不能保证结构的成员在结构中的物理位置。它们可能是字对齐的,留下空白。
在某些版本的C中有一些pragma可以明确地控制打包。
答案 1 :(得分:5)
正如其他人所提到的,除非您的编译器对齐是字节对齐的,否则您的结构可能会有“漏洞”。编译器这样做是因为它加速了内存访问。
如果你正在使用gcc,那么有一个“packed”属性会导致结构字节对齐,所以删除“hole”:
struct __attribute((__packed__)) mystruct {
uint8_t dummy1[1];
uint16_t very_important_data;
uint8_t dummy2[3];
} *mystruct = (void *) dataz;
但是,这不一定能解决问题。 16位值可能未设置为您认为应该的值,具体取决于计算机的字节顺序。您将不得不在结构中的任何多字节整数中交换字节。没有通用的功能,因为它需要有关运行时结构布局的信息,而C不提供。
将结构映射到二进制数据通常是不可移植的,即使您现在可以在机器上运行它。
答案 2 :(得分:3)
最有可能的是,编译器在dummy1
和very_important_data
之间添加了一个填充字节,以便在16位边界上对齐very_important_data
。
通常,struct
中字段的对齐和填充与实现有关,因此您不应该依赖它。如果您绝对需要特定的行为,许多编译器会提供#pragma
或其他指令来控制它。查看编译器的文档。
答案 3 :(得分:2)
这取决于编译器,但通常编译器将每个成员与其自然对齐对齐。在你遇到的情况下,very_important_data
是一个uint16_t
,可能有2个字节的自然对齐。