2014-01-15 44 views
2

我現在有以下結構精確計算在一個結構

struct foo { 
    u32 bar; 
    u8 baz[1]; 
} 

的的sizeof(結構富)被談到了將8由於填充編譯器添加填充。

如果我想要的結構的大小是準確的(在本例中爲5),我將如何以「可縮放」的方式進行計算(即不執行類似sizeof(foo.bar) + sizeof (foo.baz)的操作)?

+2

聽起來很荒謬的。在*成員之間添加*的填充會發生什麼情況? –

+0

想要非填充尺寸的原因是什麼?如果你想讓你的結構不被填充,你總是可以使用編譯器提示來實現'__attribute __((packed))',但是在大多數情況下,無填充的大小是無用的 –

+0

「可縮放」是什麼意思? –

回答

3

標準C沒有辦法計算結構體成員大小的總和,而不在源代碼中列出它們(這可能需要預處理器特性的某些幫助或使用其他代碼來完成)在最終代碼的編譯時執行]以生成最終代碼)。這是因爲標準C沒有提供任何方式來遍歷結構的成員,或者不使用其成員的名稱來檢查其組成。

我會做了C++,除了一些可怕的黑客也許可以使用模板和諸如此類的東西同樣的要求。我傾向於這是不可能的,但我還沒有檢查最新C++標準中的所有新功能。

在任何情況下,計算總大小對於序列化和反序列化成員(即將成員轉換爲網絡包中的字節,反之亦然)的陳述目的是不夠的,因爲您仍然需要單獨轉換成員,不只是知道他們的大小總數。對於接近目標

選項包括:

  • 使用實現特定的功能,包裝結構,以便它不包含任何填充。然後,您可以編寫和讀取結構的字節以執行序列化和反序列化。您仍需要以確保該類型的發送和接收系統(相同的寬度的整數,相同的格式的浮點值,相同的字符集編碼,等等)之間相匹配。對於所需類型的運行時處理
  • 編寫代碼。它需要以某種格式描述結構,例如類型列表,並且每種類型都會有個案。每種情況下,將包含編譯時代碼的類型(例如,sizeof(float)),但代碼將在運行時的情況之間分派。所以你需要用這個代碼使用的格式來準備結構的描述。
+0

我想我已經實現了一些相關的C++ 11解決方案來將壓縮數據保存在閃存中。導致生產代碼的SO問題和答案都可以找到[這裏](http://stackoverflow.com/questions/18242003/creating-an-api-metaprogramming-dsl-for-static-initialization-of-a-佈局desc)和[這裏](http://stackoverflow.com/questions/18251815/creating-an-array-initializer-from-a-tuple-or-variadic-template-parameters) –

+0

一個需要有一些手寫(或者甚至是容易生成的)映射代碼來處理我當然提到的內容...... –

1

「......在一個可擴展的方式?」 - 無法做到,C沒有內存佈局語義。

注: 我從來沒有發現一個編譯器給了「的sizeof(T)」不正確的值,

編譯器告訴你N個結構FOO對象的數組, 因爲你已經在這裏呈現,將佔用8 * N個字節。

我有,但是,在一個項目上,編碼逼「適當的」, 預期,而我們應該說一個對象, 的最緊湊「包裝」,而無需使用編譯。 (我認爲是編譯指示非 便攜。但是,每當我已經調查 編譯含有雜注,似乎提供了優異的成績。 pragma偶爾拼寫不一樣)

換句話說,如果你確實需要一個5字節的foo對象,那麼我認爲可能並且相對容易。但是,如果你 對象foo的增長有很多領域的下列方案很快得到煩人。

在我的嵌入式軟件項目中,遠程處理器在不同的編譯機器上使用了不同的處理器和不同的編譯器,他們使用編譯器的編譯指示包。

我們的軟件必須描述符合其包裝的物品,因爲已經有數千個子系統已經發貨。我們的代碼必須能夠交互。這些系統與通過專有背板發送的二進制數據包進行通信。 (C和C++都沒有提供內存佈局的語義,但Ada做了,但是有人關心了嗎?)

我們嘗試了許多解決方案,每個解決方案都帶有pro和con。

下面是5種成功機制中的1種後的樣式...這個很容易記住。

兩個步驟:

  • 聲明使用字節數組的對象。
  • 添加適當的setter和getter,每個每場1。

注意:您將瞭解字節序,看看宏的NTOH和hton

namespace fooNameSpace 
{ 
    struct foo 
    { 

    private: 
     uint8_t pfd[5]; //packed_foo_data 

    public: 
     void barSet(uint32_t value) 
     { 
      pfd[0] = static_cast<uint8_t>((value & 0xff000000) >> 24); // msbyte 
      pfd[1] = static_cast<uint8_t>((value & 0x00ff0000) >> 16); 
      pfd[2] = static_cast<uint8_t>((value & 0x0000ff00) >> 8); 
      pfd[3] = static_cast<uint8_t>((value & 0x000000ff) >> 0); // lsbyte 
     }; // you will need to fix for endian of target 

     uint32_t barGet(void) 
     { 
      uint32_t value = 0; 
      value |= pfd[0] << 24; // msbyte 
      value |= pfd[1] << 16; 
      value |= pfd[2] << 8; 
      value |= pfd[3] << 0; // lsbyte 
      return (value); 
     }; // fix for endian of target 

     void  bazSet(uint8_t value) { pfd[4] = value; }  
     uint8_t bazGet(void)   { return pfd[4]; }; 
    }; 
} 

一些我的團隊試圖建立工會... ,發現它有困惑的時候工會 必須在本地主機和編譯器上的遠程編譯器,目標字節序 匹配。有點也困惑 模擬器。