将二进制数据读入内存结构,效果奇怪

时间:2010-06-03 22:54:13

标签: c data-structures

我已经在这一段时间了,这真让我感到困惑。这是一个非常精简的代码片段,可以重现这个问题:

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;

你会如何解释?

4 个答案:

答案 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)

最有可能的是,编译器在dummy1very_important_data之间添加了一个填充字节,以便在16位边界上对齐very_important_data

通常,struct中字段的对齐和填充与实现有关,因此您不应该依赖它。如果您绝对需要特定的行为,许多编译器会提供#pragma或其他指令来控制它。查看编译器的文档。

答案 3 :(得分:2)

这取决于编译器,但通常编译器将每个成员与其自然对齐对齐。在你遇到的情况下,very_important_data是一个uint16_t,可能有2个字节的自然对齐。