---
# System prepended metadata

title: C 語言： struct
tags: [C 語言]

---

---
tags: C 語言
---

# C 語言： struct


## aligment (對齊)

>以下是以 64 位元環境下的型別大小為例。要注意這些型別會根據平台架構而有所不同。

結構體 `(struct)` 對齊的規則可分為兩個部分：

1. 結構體成員的對齊方式：
   - 根據各成員的對齊要求來對齊，如果沒有對齊則填補 padding byte
2. 整個結構體大小的對齊方式：
   - 必須是最大型別成員大小的倍數

計算順序是先滿足第一點，然後再計算第二點。
```c
struct foo1 {
    int a; // 4 bytes
    int b; // 4 bytes
    int c; // 4 bytes
};
```
在 `foo1` 中，結構體成員已對齊，且整個結構體大小也為 `int` 的倍數，因此 sizeof(struct foo1) = 12。
```c
struct foo2 {
    char a; // 1 bytes
    int b;  // 4 bytes
    int c;  // 4 bytes
};

// 加入 padding 後:
struct foo2 {
    char a; // 1 bytes
            // 3 bytes padding
    int b;  // 4 bytes
    int c;  // 4 bytes
};
```
在 `foo2` 中，為了讓 b 能夠以 4 個位元組位置對齊，因此在 a 後面加入了 3 個位元組的 padding。成員內對齊後結構體大小也為 `int` 的倍數，因此 sizeof(struct foo2) = 12。

```c
struct foo3 {
    short int a; // 2 bytes
    char b;      // 1 byte
    int c;       // 4 bytes
};

// 加入 padding 後:
struct foo3 {
    short int a; // 2 bytes
    char b;      // 1 byte
                 // 1 byte padding
    int c;       // 4 bytes
};
```
在 `foo3` 中，了讓 c 能夠以 4 個位元組位置對齊，因此在 a 後面加入了 1 個位元組的 padding。成員內對齊後結構體大小也為 `int` 的倍數，因此 sizeof(struct foo3) = 8。

```c
struct foo4 {
    char *a;         // 8 bytes
    unsigned int b;  // 4 bytes
    char c;          // 1 bytes
                     // 3 bytes padding
};
```
在 `foo4` 中，成員內部已對齊，但成員大小總和為 13，不是最大成員型別 `char *` 的倍數，因此在結構尾端會在 padding 3個位元，因此 sizeof(struct foo3) = 16。

```c
struct foo5 {
    char a;     //  1 byte
    int b;      //  4 bytes
    int c;      //  4 bytes
    char *d;    //  8 bytes
    char e[9];  //  9 bytes
    long f;     //  8 bytes
};

// 加入 padding 後:
struct foo5 {
    char a;     //  1 byte
                //  3 bytes padding
    int b;      //  4 bytes
    int c;      //  4 bytes
                //  4 bytes padding
    char *d;    //  8 bytes
    char e[9];  //  9 bytes
                //  7 bytes padding
    long f;     //  8 bytes
};
```
在 `foo5` 中，`char e[9]` 與 `long f` 對齊，因此填充至最接近的 8 位元倍數：16 位元，故有 7 位元的填充，因此 sizeof(struct foo4) = 40。


```c
struct foo6 {
    union {       // 4 bytes
        char a;
        int b;
    };
    char *c;      // 8 bytes
    int d;        // 4 bytes
    char e;       // 1 byte
};

// 加入 padding 後:
struct foo6 {
    union {       // 4 bytes
        char a;
        int b;
    };
                  // 4 bytes padding
    char *c;      // 8 bytes
    int d;        // 4 bytes
    char e;       // 1 byte
                  // 3 padding
};
```

在 `foo6` 中，union 的對齊方式也是相同的。在 `foo6` 內的 union 大小為 4 個位元，因此與 `char *c` 對齊為 8 個位元。成員的位元總和為 21，需要與 `char *` 的 8 位元倍數對齊，因此 sizeof(struct foo5) = 24。

```c
union foo7 {
    struct {
        char a[2];
        long b;
        char c;
    };
    char *d;
    char e;    
};

// 加入 padding 後:
union foo7 {
    struct {
        char a[2];  // 2 bytes
                    // 6 bytes padding
        long b;     // 8 bytes
        char c;     // 1 byte
                    // 7 bytes padding
    };
    char *d;
    char e;    
};
```
同理，sizeof (union foo7) = 24。

## bit field

引述 [維基百科](https://zh.wikipedia.org/zh-tw/%E4%BD%8D%E6%AE%B5) 介紹:
位元欄（或稱「位域」，Bit field）為一種資料結構，可以把資料以位元的形式緊湊的儲存，並允許程式設計師對此結構的位元進行操作。這種資料結構的好處：
- 可以使資料單元節省儲存空間，當程式需要成千上萬個資料單元時，這種方法就顯得尤為重要。
- 位元欄可以很方便的訪問一個整數值的部分內容從而可以簡化程式原始碼。

下面用一些例子來觀察 `bit field` 的行為。

```c
struct foo1 {
  int    a:4;  // 4 bits
  int    b:4;  // 4 bits
  int    c:4;  // 4 bits
  int    d:4;  // 4 bits
};

int i = 0xFA; // 11111010;
struct foo1 *f1 = (struct foo1 *)&i;
// sizeof (struct foo1) = 4
```

我們以 GDB 來觀察
```shell
(gdb) p *f1
$1 = {a = -6, b = -1, c = 0, d = 0}
(gdb) x/4tb f1
0x7fffffffe22c:  11111010  00000000  00000000  00000000
```

這邊可以得到下面結果：
&nbsp;&nbsp;<font color=#32AA6A>b</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#CC3BCC>a</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;d&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c
<font color=#32AA6A>1111</font>&nbsp;<font color=#CC3BCC>1010</font>&nbsp;&nbsp;0000 0000

```c
struct foo2 {
  int    a:4;
  int    b:4;
  int    c:4;
  int    d:4;
  int     :0; // zero-length bit-field
  int    e:4;
  int    f:4;
};

int i[] = {0xABCD, 0x00EF};
struct foo2 *f2 = (struct foo2 *)&i;
```

此外，還有一種 `zero-length` `bit-field`，像在 foo2 結構中所示，這會讓從 `e` 開始的成員從另一個記憶體位置開始。

以 GDB 來觀察
```shell
(gdb) p *f2
$1 = {a = -3, b = -4, c = -5, d = -6, e = -1, f = -2}
(gdb) x/8tb f2
0x7fffffffe230:  11001101  10101011  00000000  00000000  11101111  00000000  00000000  00000000
```

&nbsp;&nbsp;&nbsp;<font color=#32AA6A>b</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#32AA6A>a</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#32AA6A>d</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#32AA6A>c</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#CC3BCC>f</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#CC3BCC>e</font>
<font color=#32AA6A>1100</font>&nbsp;<font color=#32AA6A>1101</font>&nbsp;<font color=#32AA6A>1010</font>&nbsp;<font color=#32AA6A>1011</font>&nbsp;<font color=#32AA6A>00000000 00000000</font>&nbsp;&nbsp;<font color=#CC3BCC>1110</font>&nbsp;<font color=#CC3BCC>1111</font>&nbsp;<font color=#CC3BCC>00000000 00000000</font>

另外也可以從 sizeof(struct foo2) 來觀察：
- 有 zero-length bit-field, sizeof(struct foo2) = 8
- 沒有 zero-length bit-field, sizeof(struct foo2) = 4