2017-01-23 197 views
2
struct something { 
    uint32_t a; 
    uint8_t b; 
    uint16_t c; 
    uint32_t x; 
    uint16_t y; 
    uint8_t z; 
}; 

uint8_t *data = malloc(14); 
struct something *s = (struct something *)data; 

s->a = 1; 
s->b = 2; 
s->c = 3; 
s->x = 4; 
s->y = 5; 
s->z = 6; 

在C或結構填充中執行此操作總是安全可能會導致問題嗎?保證數據[0] ..數據[3] == 1,數據[4] == 2,數據[5] ..數據[6]數據[3] == 1,數據[4] == 3, == 3,data [7] .. data [10] == 4,data [11] .. data [12] == 5和data [13] == 6?C結構填充問題

編輯2:上編輯1

+1

你是否檢查了'sizeof'這個'struct'ure? –

+1

不,它不安全。填充*會*成爲問題。 –

+1

編輯之後的評論:不,這不是因爲可能的填充。 –

回答

10

這個小錯誤是安全運行。編譯器將以實現定義的方式添加填充。

一般來說,一個給定尺寸的構件將上一對齊偏移是大小的倍數。

鑑於典型方式填充完成,這struct將最有可能在16個字節大小。物理佈局很可能(但不一定)如下所示:

struct something { 
    uint32_t a;   // offset 0 
    uint8_t b;   // offset 4 
         // 1 byte padding 
    uint16_t c;   // offset 6 
    uint32_t x;   // offset 8 
    uint16_t y;   // offset 12 
    uint8_t z;   // offset 14 
    // 1 byte padding 
}; 

請勿使用幻數。 Intead,請使用sizeof運營商。

struct something *s = malloc(sizeof(struct something)); 

編輯:

如果你想提高你的struct設計了一個特殊的方式的機會,看到這個guide to structure packing。如果你遵循這裏的做法,那麼很有可能(但不是100%)你的struct將按照你的期望被記憶。

對於gcc,您可以使用__attribute__((packed))上的struct刪除struct的填充。但是,這樣做可能會導致性能損失或可能導致頁面錯誤。 -Wpadded-Wpacked選項也可以告訴你更多關於填充。

+1

但是,如果有這樣的排序和消除填充是一個**要求**,那麼您的編譯器_may_有工具可以幫助您(但您付出了可移植性損失並可能是性能損失)。 –

+0

這些偏移量是否會反映存儲在「數據」變量中的數據?我編輯這個問題來說清楚。我的意思是,編譯器是否足夠智能以繞過由填充引起的問題? –

+0

@RafaelAlmeida,沒有問題是由填充造成的,除非你做了明確依賴於填充位置或數量的東西。需要做這樣的事情是不常見的。沒有這樣做的程序嚴格符合標準。取決於關於存儲器偏移的假設,除了第一個與結構的開始相關的結構成員之外,例如您在編輯中詢問的內容,正是這種不合格的事情。 –

2

硬編碼14 非常調皮

你爲什麼不使用sizeof(struct something)呢?這是一個編譯時可評估的常量表達式,因此不存在運行時開銷。

你的預感是正確的:一個編譯器保留插入結構元件之間填充,以及在最終元件的端部的右側。 (請注意,第一個元素的地址必須是一樣的struct的地址雖然)。