簡體   English   中英

為什么打包結構5的大小而不是4個字節?

[英]Why is the size of packed struct 5 instead of 4 bytes here?

參見在線示例: Ideone示例

struct {
  union {
    struct {
      uint32_t messageID : 26;
      uint8_t priority : 3; 
    } __attribute__ ((packed));
    uint32_t rawID : 29;
  } __attribute__ ((packed));
  uint8_t canFlags : 3;
} __attribute__ ((packed)) idSpecial;

為什么編譯器會將結構的大小報告為5個字節而不是4個? 它應該包含32位。

問題是__attribute__((packed))不執行按位打包。 它只是保證struct成員之間沒有填充。 您可以嘗試這個更簡單的示例,其中size也報告為5:

typedef struct structTag {
    struct {
      uint32_t messageID : 26;
      uint8_t priority : 3;
    } __attribute__ ((packed));
    uint8_t canFlags : 3;
} __attribute__ ((packed)) idSpecial;

按位打包僅適用於位域成員。 您需要將結構重新設計為結構與bitfields messageID / priority / canFlags的結合,以及帶有bitfields rowID / canFlags的結構。 換句話說,您需要有一些重復或使用訪問器宏或成員函數。

這是因為內存對齊:編譯器不會在一個字節的中間啟動canFlags ,它會在下一個字節的開頭(可能是*)啟動它。 因此,初始聯合有四個字節, canFlags有一個字節。

例如,如果你將canFlags移動到聯合中,那么它(可能是*)的大小為4:

typedef struct structTag {
  union {
    struct {
      uint32_t messageID : 26; /* 26bit message id, 67108864 ids */
      uint8_t priority : 3; /* priority: MUST BE 0 */
    } __attribute__ ((packed));
    uint32_t rawID : 29;
    uint8_t canFlags : 3; /* <==== Moved */
  } __attribute__ ((packed));
} __attribute__ ((packed)) idSpecial;

有關ideone的更新示例 顯然,這種具體的變化可能不是你想要的; 我只是在證明問題是在字節邊界上開始一個新字段。


*“可能”因為最終取決於編譯器。

使用數據結構對齊在計算機存儲器中排列和訪問數據。 其中有兩個相關問題

  1. 對准
  2. 填充

當計算機執行寫操作時,它通常以4個字節的多個字節寫入(對於32位系統)。 這一行為的一個原因是提高績效的目標。 因此,當您編寫任何數據結構時,它具有前1個字節變量,然后是4個字節的可變數據,它將在第一個1字節數據之后執行填充,以在32位邊界上對齊它。

struct {
  union {
    struct {
      uint32_t messageID : 26;
      uint8_t priority : 3; 
    } __attribute__ ((packed));
    uint32_t rawID : 29;
  } __attribute__ ((packed));
  uint8_t canFlags : 3;
} __attribute__ ((packed)) idSpecial;

現在在上面的數據結構中你正在使用__attribute__ ((packed)) ,這意味着沒有填充。 所以uint32_t是4個字節,但是你說它有26位和3位優先級。 現在,因為在一個結構中有兩個變量,所以它將保留32位而不是29位,這樣您的第一個結構的信息就會在邊界上進行分配。

現在對於canFlags它將需要另一個字節。 所以這使得5個字節而不是4個字節。

在某些編譯器中,要“合並”這些位,所有項必須是相同的類型。 所以把它uint32_t你現在有uint8_t - 這似乎不是編譯器中的情況IdeOne使用tho'

[無論如何,編譯器如何合並這些位仍然取決於它,因此絕對保證數據存儲為32位的唯一方法是使用單個uint32_t並聲明一個執行相關移位的類。 anding / oring來操縱值 - 唯一的保證是你的struct中的ONE元素至少有你所要求的位數]

正如其他人所指出的那樣,除了字節邊界之外,你無法啟動新結構。 我通過在union中包含第二個結構來修復它,如下所示: http//ideone.com/Mr1gjD

#include <stdint.h>
#include <stdio.h>

typedef struct structTag {
  union {
    struct {
      uint32_t messageID : 26; /* 26bit message id, 67108864 ids */
      uint8_t priority : 3; /* priority: MUST BE 0 */
    } __attribute__ ((packed));
    struct {
      uint32_t rawID : 29;
      uint8_t canFlags : 3;
    };
  } __attribute__ ((packed));
} __attribute__ ((packed)) idSpecial;

int main() {
    printf("size: %d", sizeof(idSpecial));
    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM