c ++标准保证的二进制数据布局

时间:2013-12-10 14:00:07

标签: c++

这纯粹是一个理论问题,我没有真正发现自己,但它激起了我的好奇心,想看看是否有人有更好的解决方案:

  

如何可移植地保证特定的文件格式/网络   协议或任何符合特定位模式的协议。

假设我们的文件格式使用64位头结构,紧接着是32位结构的可变长度数组:

Header:  magic : 32 bit
         count : 32 bit

Field :  id   : 16 bit
         data : 16 bit

我的第一直觉是写下这样的东西:

struct Field
{
    uint16_t id   ;
    uint16_t data ;
};

除了我们的编译器可能决定填充是可取的,我们最终得到64位结构。所以我们的下一个赌注是:

using Field = uint16_t[2];

并继续努力。

也就是说,除非有人仔细阅读标准并注意到uint16_t是可选的。在这一点上,我们的下一个最好的朋友是uint_least16_t,它保证至少 16位长,但是对于我们所知,在10位/字符处理器中我们知道可能是20位长。

此时,我能想出的唯一真正的解决方案是某种比特流,能够读取和写入特定数量的比特,并且可以通过std :: numeric_limits进行调整。

那么,是否有人非常仔细阅读标准并发现我遗漏的一点?或者这是拥有便携式保证的唯一真正方式。

注意:   - 我刚刚意识到字节序可能会增加另一层复杂性。   - 我正在使用ISO标准的当前工作草案(N3797)。

4 个答案:

答案 0 :(得分:2)

如果您想确保所有标准符合性的可移植性,包括CHAR_BITS不是8的平台,那么,您已经为您完成了工作。< / p>

如果您愿意将自己限制在98%将要编程的计算机上,我建议为必须遵守特定线路格式的任何内容编写显式序列化。这包括将整数分成字节等等。

围绕事物编写适当的抽象,代码也不会太糟糕。不要在任何地方放置班次和面具。封装它。

答案 1 :(得分:2)

  

如何可移植地保证特定的文件格式/网络   协议或任何符合特定位模式的协议。

你做不到。不是在C ++中,它是针对抽象平台标准化的,只能假设存在由位组成的“字节”。我们甚至无法肯定地说,在标准中只查看 char中有多少位。您可以使用位域来处理所有内容,因为位是不可分割的,但是您至少可以使用填充来对抗。

有时最好是为了符合性而放弃绝对标准一致性的想法,并寻求其他方法来有效地完成工作。在这种情况下,平台细节与​​几乎绝对标准一致性(又名,良好的编程实践)相结合将使您自由。

我经常处理的每个平台(linux和windows)都提供了一种方法来规范编译器实际应用的填充。对于网络通信,在Linux&amp;我使用的Windows:

#pragma pack (push, 1)

作为我要通过网络发送的所有数据结构的前言。 Endianness确实是另一个挑战,但是或多或少可以轻松地使用每个平台提供的其他资源:ntohl等。

标准一致性是一个值得称赞的目标,实际上在代码审查中,我会拒绝大多数不符合要求的代码。然而,缺乏一致性实际上只是拒绝的绰号;不是原因本身。拒绝的实际原因很大程度上是在移动到另一个平台时维护和移植不符合代码的难度,或者甚至只是在同一平台上升级编译器。不符合代码的代码可能会编译甚至看似可行,但即使经过全面测试,它也会在您最不期望的时候以微妙和悲惨的方式失败。

故事的寓意是:

  

除了你以外,你应该总是编写符合标准的代码   不应该。

这真的只是爱因斯坦对奥卡姆剃刀表达的重新想象:

  

让一切尽可能简单,但并不简单。

答案 2 :(得分:0)

我会使用网络类型和网络字节顺序。请看这个链接。http://www.beej.us/guide/bgnet/output/html/multipage/htonsman.html。该示例使用uint16_t。您可以一次写入一个字段以防止填充。 或者,如果您想要读取和写入整个结构,请参阅此链接C++ struct alignment question

答案 3 :(得分:0)

使程序易于使用。

提供输入方法,从输入中提取数据并写入数据成员。这消除了填充,对齐边界和字节序的问题。与输出类似。

例如,如果输入数据为16位宽,但平台为32位宽,则使用32位字段声明结构。将输入中的16位复制到32位字段中。

大多数程序读入结构的次数少于访问数据成员的次数。您的程序没有100%的时间读取输入。