是否应将所有预期从二进制读取的结构标记为已打包?

时间:2017-02-02 17:36:57

标签: c data-structures memory io

我知道某些结构,可能或可能不会在元素之间添加填充。

我当前的项目是从/ dev / input文件读取输入。这些文件的二进制布局在<linux/input.h>

中定义
struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};

我想知道这个结构没有标记打包属性。这意味着/ dev / input文件(逐位打包)不能保证与struct相同的包匹配。因此逻辑

struct input_event event;
read(fd, &event, sizeof(event));

未定义适用于所有拱门。

我的逻辑中有谬误吗?或者可以安全地假设某些事情不会被打包?

2 个答案:

答案 0 :(得分:2)

按布局打包

在目前的情况下,你是安全的。你的struct input_event已经完全打包了。

struct timeval time;  /* 8 bytes */
__u16 type;           /* 2 bytes */
__u16 code;           /* 2 bytes */
__s32 value;          /* 4 bytes */

这意味着,成员形成干净的32位块,因此不需要填充。这个article解释了struct成员(特别是字符)的大小及其布局如何影响填充,从而影响结构的最终大小。

预处理器打包

通过预处理器打包结构似乎是一个很好的解决方案。看得更近一些,会出现几个缺点,其中一个在你关心的点上点击你(参见#pragma pack effect

效果

Padding确保您的struct成员可以在不搜索内存块的情况下访问(32位机器上的4字节块和64位机器上的8字节块)。因此,打包这样的结构会导致成员跨越多个内存块,因此需要机器搜索它们。

不同的平台(Archs,Compilers)

预处理器指令主要针对供应商和架构。因此,使用它们会导致较少或最坏情况下代码不可移植。

结论

正如本article(上文已经提到)的作者所述,甚至NTP也直接从网络中读取数据到结构中。 因此,仔细布置您的结构并可能手工填充它们可能是最安全,也是最便携的解决方案。

答案 1 :(得分:1)

如果你坚持从二进制图像直接将结构加载到内存中,那么C不是你的朋友。允许填充,基本类型可以具有不同的宽度和字节顺序。您甚至不能保证一个字节中的8位。然而,打包结构并坚持int32_t等将有很大帮助,它有效地便携式bar endiannness。

但最好的解决方案是从流中加载结构。这甚至可以通过实数来实现,尽管有点繁琐。

这是如何以便携式读取16位整数。有关其余功能,请参阅我的github项目(类似逻辑) https://github.com/MalcolmMcLean/ieee754

/**
  Get a 16-bit big-endian signed integer from a stream.
  Does not break, regardless of host integer representation.
  @param[in] fp - pointer to a stream opened for reading in binary mode
  @ returns the 16 bit value as an integer
*/
int fget16be(FILE *fp)
{
    int c1, c2;

    c2 = fgetc(fp);
    c1 = fgetc(fp);

    return ((c2 ^ 128) - 128) * 256 + c1;
}