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

San*_*war 6 c memory io data-structures

我知道有些结构,可能会,也可能不会,在元素之间添加填充.

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

struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};
Run Code Online (Sandbox Code Playgroud)

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

struct input_event event;
read(fd, &event, sizeof(event));
Run Code Online (Sandbox Code Playgroud)

未定义为跨所有拱门工作.

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

小智 1

按布局包装

在目前的情况下,你是安全的。您的 struct input_event 已准备好打包。

struct timeval time;  /* 8 bytes */
__u16 type;           /* 2 bytes */
__u16 code;           /* 2 bytes */
__s32 value;          /* 4 bytes */
Run Code Online (Sandbox Code Playgroud)

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

由预处理器打包

乍一看,通过预处理器打包结构似乎是一个很好的解决方案。仔细观察,会发现有几个缺点,其中一个在您关心的地方击中了您(另请参阅#pragma packeffect

表现

填充可确保您的结构成员无需搜索内部内存块(分别为 32 位计算机上的 4 字节块和 64 位计算机上的 8 字节块)即可访问。因此,打包此类结构会导致成员跨越多个内存块,因此需要机器搜索它们。

不同的平台(Arch、编译器)

预处理器指令很大程度上取决于供应商和体系结构。因此,使用它们会导致代码的不可移植性降低或在最坏的情况下。

结论

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

  • 您的答案提出了许多需要澄清的陈述:“由预处理器打包” - 预处理器对`pragma`不执行任何操作,即使关键字是*指令* - `#pragma pack`背后的所有工作都已完成由编译器。而“在内存块内搜索”是无稽之谈。运行时从不“搜索”内存地址 - 它使用基地址的常量偏移量。在某些体系结构上,“未对齐访问”会受到一些惩罚 - 也许您指的是这一点。顺便说一句:是否要插入填充字节取决于特定的编译器。 (2认同)