2011年8月29日 星期一

alignment in the C

在建立struct時,有時候裡面的成員的排序很重要。
排序不同,會造成sizeof整個struct的值不同。

例如以下的例子:
struct aaaaa
{
  char a;
  int b;
  char c;
};

-byte數為12-

struct aaaaa
{
  char a;
  char c;
  int b;
};

-byte數為8-

========== START ==========

struct 的 alignment 有兩個, 首先是個別成員的 alignment, 然後是整個 struct 的 alignment.

首先, 每一個類型都會有它自己的 alignment requirement. 每個物件在記憶體裏的相對位置是:
  Offset % alignment-requirement == 0

假定在你的編譯器上, char 的 alignment requirement 是 1, int 的 alignment requirement 是 4.

在第一個 struct 裏, a 是在 offset 0 的位置, 而 b 則須在 offset 4 的位置. a 跟 b 之間會有 3 個 byte 的空格.

個別成員的 alignment 完成後就要做整個 struct 的 alignment. 這個關係到最後一個成員後面要加多少個 padding. 需要這個的原因是當你以 struct 來定義一個陣列的時候, 每一個 struct 元素都需要 align. 這時就要以到 struct 裡面最大的成員來做決定.

所以第一個 struct 最後一個成員 c 後面需要 3 個 bytes 的 padding. 這樣在陣列的架構下, 下一個元素的 b 也會 align:

    +=======+
  0 |a|x|x|x|
    |-------|
  4 |b      | 
    |-------|
  8 |c|x|x|x|
    +=======+
 12 |a|x|x|x|
    |-------|
 16 |b      |
    |-------|
 20 |c|x|x|x|
    +=======+
    |       |

第二個 struct 的 padding 結構:

    +=======+
  0 |a|c|x|x|
    |-------|
  4 |b      |
    +=======+
  8 |a|c|x|x|
    |-------|
 12 |b      |
    +=======+

而要解決此問題,最好在每一個struct裡面都放相同alignment的變數,
或是使用#pragma pack ()
A1:
#pragma pack(push, 1)
struct aaaaa
{
  char a;
  char c;
  int b;
};
#pragma pack(pop)

其中 "push" 的意思是先將原來的設定(可能是 4)暫存起來;而 "pop" 則是恢復原來的設定。

A2:
#pragma pack(1)
struct aaaaa
{
  char a;
  char c;
  int b;
};
#pragma pack(1)

意思是說,強制把alignment設定為1 byte

參考資料:
Visual C++ 的 Struct Memeber Alignment 問題與解決方法
C/C++ struct 的 alignment

沒有留言: