2012-02-17 79 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的聲明,使其與文件格式相匹配,並直接讀寫。這樣做意味着您的代碼不能移植到某些系統。你必須決定你願意支付的價格。