2012-02-17 63 views
1

我一直有这个愚蠢的结构很多麻烦。我不明白为什么会这样做,我真的不知道如何解决它。我知道如何解决这个问题的唯一方法就是去除结构并以其他方式进行(我不想这么做)。构造奇怪 - C++

因此,我正在从一个文件读取数据,并且我正在一次读取一个结构指针。看起来,我的'long long'的偏移/指针每次都会搞砸。在下面详细查看。

因此,这里是我的结构:

struct Entry 
    { 
     unsigned short type; 
     unsigned long long identifier; 
     unsigned int offset_specifier, length; 
    }; 

这里是我阅读所有的垃圾进入结构指针/阵列码:

Entry *entries = new Entry[SOME_DYNAMIC_AMOUNT]; 
fread(entries, sizeof(Entry), SOME_DYNAMIC_AMOUNT, openedFile); 

正如你所看到的,我写的所有的到我的结构数组中。现在,我将向您展示我正在阅读的数据(对于此示例中的第一个结构)。

The bytes I am reading.

因此,这是进入的第一个元素在“项”数据。第一个项目(简称“类型”)看起来很好。在那之后,当读取'标识符'时,整个结构看起来像移位X个字节。下面是所述第一元件的一个图像(反转端之后):

enter image description here

这里是在存储器中的数据(红色正方形是它开始的地方):

enter image description here

我知道这有点令人困惑,但我试图尽可能好地解释它。感谢您的帮助,Hetelek。 :)

+1

您没有显示结构如何*写*。 – tenfour 2012-02-17 04:38:05

回答

6

结构用额外的字节填充,以便字段访问速度更快。您可以防止这种情况与#pragma pack

#pragma pack(push, 1) 

struct Entry 
{ 
    /* ... */ 
}; 

#pragma pack(pop) 

请注意,这可能不是100%便携式(我知道至少GCC和MSVC支持用于x86)。

+0

你的意思是说这在Macintosh/Linux上无法正确运行?因为我正计划通过Linux和Mac移植它。此外,感谢您的快速反应,但我觉得有必要有更好的方式来做到这一点...虽然不知道。你如何看待你在上面张贴的方式?高效? – hetelek 2012-02-17 04:41:37

+1

你可能会在Linux/Mac上使用GCC,所以你会没事的。如果你想要使用一个结构体,它将获得最高效率(除非你保持填充状态并且一次只读一个字段)。 – 2012-02-17 04:42:56

3

用二进制读写结构文件是危险的。

您遇到的问题是编译器会在结构的typeidentifier成员之间插入填充(对齐需要)。显然无论程序写了的数据(你没有告诉我们有关)使用了不同的布局,该程序试图读取的数据。

如果两个系统(写入数据和读取数据的系统)具有不同的对齐要求并因此对于Entry类型的布局不同,则可能会发生这种情况。

虽然对齐并不是唯一的潜在问题; endianness的差异也可能是一个严重的问题。对于预定义的整数类型,不同的系统可能有不同的大小。你不能假设struct Entry将有一个一致的布局,除非所有处理它的代码运行在一个单一的系统上 - 理想情况下使用相同版本的同一个编译器。

可能能够使用#pragma pack来解决这个问题,但我不推荐它。它不是便携式的,并且it can be unsafe。充其量,它将解决成员之间的填充问题;仍然有很多方法可以根据系统的不同而变化。

如果不知道您正在阅读的文件的数据布局在哪里以及如何定义,那么无法为您提供明确的解决方案。

如果我们假设每个记录的文件的布局是,例如:

  • 网络字节顺序的2字节的无符号整数(type
  • 网络字节顺序的8字节整数(identifier
  • 网络字节顺序一个4字节整数(offset_specifier, length
  • 他们
  • 之间没有填充

那么应该将数据读取到一个缓冲unsigned char[],或进入uint16_t类型,uint32_t,和uint64_t(在<cstdint><stdint.h>定义)的对象,然后将其从网络字节顺序到本地字节顺序转换。

您可以将此转换封装在从文件中读取并转换数据的函数中,并将其存储在Entry结构中。

如果你可以假设程序只能运行在一组受限制的系统上,那么你可以绕过这些。例如,您可能可以调整struct Entry的声明,使其与文件格式相匹配,并直接读写。这样做意味着您的代码不能移植到某些系统。你必须决定你愿意支付的价格。