我知道某些结构,可能或可能不会在元素之间添加填充。
我当前的项目是从/ 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));
未定义适用于所有拱门。
我的逻辑中有谬误吗?或者可以安全地假设某些事情不会被打包?
答案 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字节块)。因此,打包这样的结构会导致成员跨越多个内存块,因此需要机器搜索它们。
预处理器指令主要针对供应商和架构。因此,使用它们会导致较少或最坏情况下代码不可移植。
正如本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;
}