---
# System prepended metadata

title: 'C Language / Structures, Unions, Bit Manipulation and Enumerations'
tags: [C Language]

---

# C Language / Structures, Unions, Bit Manipulation and Enumerations

## Ch01 / 結構（Structures）

結構是關聯變數的集合體，可包含多種不同資料型態的變數。結構常用於定義儲存在檔案中的記錄，結合指標後有助於形成更複雜的資料結構，如 Linked-List、Queue、Heap 或是 Tree。

### 定義

使用 `struct` 關鍵字定義結構，後接結構標籤名稱，再定義成員變數。例如：

```c=
struct card {
    char *face; 
    char *suit;
};
```

### Keyword： typedef

`typedef` 提供了一個特殊的機制，來為之前定義的資料型別建立同義字（aliases），structures 通常就會使用 `typedef` 來定義較簡短的名稱。例如：

```c=
typedef struct card {
    char *face; 
    char *suit;
}Card;

Card aCard; // 等同 struct card aCard;
```

上面的語句定義了一個新的型別名稱 `Card` 作為 `struct card` 的同義字。如果使用 `typedef` 來定義結構型別，其實就不需要另外的結構名稱ㄌ。例如：

```c=
typedef struct {
    char *face;
    char *suit;
} Card;
```

現在我們就可以使用 `Card` 來宣告 `struct card` 型別的變數。`typedef` 並不會建立新的型別，而只是為現有的型別建立一個新名稱作為別名。


### 初始化
```c=
typedef struct {
    char *face;
    char *suit;
} Card;

Card aCard = {"Three", "Hearts"};
```

如果初始化列表數目比成員變數少，剩餘成員會自動初始化為0或NULL。

### 存取結構成員

使用點運算子（`.`）或箭頭運算子（`->`）存取結構成員。

- `aCard.suit` 存取 `aCard` 結構的 `suit` 成員
- `aCardPtr->suit` 存取 `aCardPtr` 所指向的結構之`suit`成員

### 範例： 撲克牌洗牌發牌
```c=
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SUITS 4
#define FACES 13
#define CARDS 52

// 撲克牌結構 => 自定義資料型態
typedef struct {
    const char *face;
    const char *suit; 
} Card;

// 讓撲克牌是一個陣列
static const Card deck[CARDS] = {
    // 裡面就那四種suit跟十三種face，但懶得寫ㄌ
};

void shuffle(Card *wDeck);
void deal(const Card *wDeck);

int main(void) {
    Card shuffledDeck[CARDS];

    // 複製牌組
    for (int i = 0; i < CARDS; i++) {
        shuffledDeck[i] = deck[i];
    }

    shuffle(shuffledDeck);
    deal(shuffledDeck);
}

void shuffle(Card *wDeck) {
    // 懶得寫ㄌ...
}

void deal(const Card *wDeck) {
    // 懶得寫ㄌ...
}
```

## Ch02 / 聯合 （Unions）

聯合是一種特殊的資料結構，其所有成員共用相同的記憶體空間。因為在程式不同情況下，有些變數可能不會被使用，可使用聯合共用記憶體空間而不浪費。

### 定義

聯合定義格式與結構相同，只是使用 `union` 關鍵字。例如：

```c=
union number {
    int x;
    double y;
};
```

### 初始化＆使用

**初始化聯合時，只能使用第一個成員的型態**。存取聯合成員的方法也與結構類似，使用點運算子或箭頭運算子。示範聯合的範例：

```c=
#include <stdio.h>

// 定義聯合
union Value {
    int int_val;
    float float_val;
    char char_val;
};

int main() {
    union Value value;
    
    value.int_val = 35120; // 初始化
    /* 
     * 繼續將其他成員賦值
     *                 */
    
    printf("int_val: %d\n", value.int_val);
    printf("float_val: %f\n", value.float_val);
    printf("char_val: %c\n", value.char_val);

    return 0;
} 

// 範例輸出結果因實作而異
```

## Ch03 / 位元操作 （Bitwise Operations）

### 位元運算子說明

C 語言提供位元運算子用於操作整數的二進位位元，主要運算子有：

| 運算子 | 說明 |
| --- | --- |
| 位元AND（`&`）| 對兩個位元串進行 AND 運算。兩者都為1時，結果位元才為1，否則為0。|
| 位元OR （`\|`）| 對兩個位元串進行 OR 運算。只要任意位元有一個為1，結果位元就為1，否則為0。|
| 位元XOR（`^`）| 對兩個位元串進行互斥 OR 運算。當對應位元不同時，結果位元為1，否則為0。| 
| 位元補數（`~`）| 返回操作數的位元補數，即將0位變為1，將1位變為0。|
| 左移 （`<<`）| 二進位運算子，將一個數的各二進位全部左移若干位（最左邊的二進位丟棄，右邊補 0）。|
| 右移 （`>>`）| 二進制運算子，將一個數的各二進位全部右移若干位，正數左補 0，負數左補 1，右邊丟棄。|

這些運算子通常與無號整數運用。可使用 `printf` 的位元輸出格式說明碼顯示位元運算效果。

### 位元運算子的操作

#### 1. AND（`&`）
```c=
#include <stdio.h>

int main() {
    unsigned int a = 0x000F; // 0000 0000 0000 0000 0000 0000 0000 1111
    unsigned int b = 0x3333; // 0011 0011 0011 0011 0011 0011 0011 0011
    unsigned int c = a & b;  // 0000 0000 0000 0000 0000 0000 0000 0011

    printf("a = 0x%X\n", a); // 輸出 a = 0x000F
    printf("b = 0x%X\n", b); // 輸出 b = 0x3333 
    printf("a & b = 0x%X\n", c); // 輸出 a & b = 0x0003

    return 0;
}
```

**Output:**
:::success
**a = 0x000F  
b = 0x3333  
a & b = 0x0003**
:::

#### 2. OR（`|`）
```c=
#include <stdio.h>

int main() {
    unsigned int a = 0x000F; // 0000 0000 0000 0000 0000 0000 0000 1111
    unsigned int b = 0x3330; // 0011 0011 0011 0000 0011 0011 0000 0000
    unsigned int c = a | b;  // 0011 0011 0011 0011 0011 0011 0000 1111

    printf("a = 0x%X\n", a);
    printf("b = 0x%X\n", b);
    printf("a | b = 0x%X\n", c);

    return 0;
}
```

**Output:**
:::success
**a = 0x000F  
b = 0x3330  
a | b = 0x333F**
:::

#### 3. 位元XOR（`^`）
```c=
#include <stdio.h>  

int main() {
    unsigned int a = 0x5AA5; // 0101 1010 1010 0101
    unsigned int b = 0xF0F0; // 1111 0000 1111 0000 
    unsigned int c = a ^ b;  // 1010 1010 0101 0101

    printf("a = 0x%X\n", a);
    printf("b = 0x%X\n", b);
    printf("a ^ b = 0x%X\n", c);

    return 0;
}
```
**Output:**
:::success
**a = 0x5AA5  
b = 0xF0F0  
a ^ b = 0xA5A5**
:::

#### 4. 位元補數（`~`）
```c=
#include <stdio.h>

int main() {
    unsigned int a = 0x5AA5; // 0101 1010 1010 0101
    unsigned int b = ~a;     // 1010 0101 0101 1010

    printf("a = 0x%X\n", a);
    printf("~a = 0x%X\n", b);

    return 0;
}
```

**Output:**
:::success
**a = 0x5AA5  
~a = 0xA55A**
:::

#### 5. 左移（`<<`）
```c=
#include <stdio.h>

int main() {
    unsigned int a = 0x5AA5; // 0101 1010 1010 0101
    unsigned int b = a << 4; // 1010 1010 0101 0000

    printf("a = 0x%X\n", a);
    printf("a << 4 = 0x%X\n", b);

    return 0;
}
```

**Output:**
:::success
**a = 0x5AA5
a << 4 = 0x5AA50**
:::


#### 6. 右移（`>>`）
```c=
#include <stdio.h>

int main() {
    unsigned int a = 0x5AA5;  // 0101 1010 1010 0101
    unsigned int b = a >> 4;  // 0000 0101 1010 1010

    signed int c = -1;        // 1111 1111 1111 1111 1111 1111 1111 1111
    signed int d = c >> 4;    // 1111 1111 1111 1111 1111 1111 1111 1111

    printf("a = 0x%X\n", a);
    printf("a >> 4 = 0x%X\n", b);
    printf("c = %d\n", c);
    printf("c >> 4 = %d\n", d);

    return 0;
}
```
**Output:**
:::success
**a = 0x5AA5  
a >> 4 = 0x5AA  
c = -1  
c >> 4 = -1**
:::

## Ch04 / 位元欄位 （Bit Fields）

C 語言允許指定 unsigned int、int 結構或聯合成員所佔的位元數量，稱為位元欄位；位元欄位可以更有效地利用記憶體。

<img src= "https://learn.microsoft.com/zh-tw/cpp/cpp/media/vc38uq2.png?view=msvc-170" style="border: 2px solid grey;">




### 定義
基本定義方法就是：
```c=
struct bitCard {
    unsigned int face : 4;  // 0-12 (4位元)
    unsigned int suit : 2;  // 0-3 (2位元) 
    unsigned int color : 1; // 0-紅 1-黑 (1位元)
};
```

也可定義未命名的位元欄位作為填充物，未命名的0位元欄位，在新的存儲單元對齊下一個位元欄位。

```c=
struct example {
    unsigned int a : 13;
    unsigned int : 19;  // 19位元的未命名填充欄位
    unsigned int b : 4;
};
```

### 初始化＆使用

```c=
#include <stdio.h>

// 定義位元欄位結構
struct bitCard{
    unsigned int face : 4;
    unsigned int suit : 2;
    unsigned int color : 1;
};

int main(void){
    struct bitCard deck[52];

    // 初始化位元欄位陣列
    for (int i = 0; i < 52; i++)
    {
        deck[i].face = i % 13;
        deck[i].suit = i / 13;
        deck[i].color = (i / 13) % 2;
    }

    // 顯示第7張牌資訊
    printf("Card7: %d of ", deck[6].face);

    switch (deck[6].suit)
    {
    case 0:
        printf("Spades");
        break;
    case 1:
        printf("Hearts");
        break;
    case 2:
        printf("Diamonds");
        break;
    case 3:
        printf("Clubs");
        break;
    }

    printf(", %s\n", deck[6].color ? "Red" : "Black");

    return 0;
}
```

**Output:**
:::success
**Card7: 6 of Spades, Black**
:::

## Ch05 / 列舉 （Enumerations）

列舉是一組以識別字命名的整數常數集合，使用 `enum` 關鍵字定義。

### 定義

以下範例會建立一個新的 `enum months` 型態，識別字從0開始對應至12。

```c=
enum months {
    JAN, FEB, MAR, APR, MAY, JUN, 
    JUL, AUG, SEP, OCT, NOV, DEC 
};
```

也可以明確指定識別字的對應值：

```c=
enum ranks { CUSTOMER = 1, MANAGER = 10, EXECUTIVE = 100 }; 
```

### 範例

```c=
#include <stdio.h>

enum Months {
    JAN = 1, FEB, MAR, APR, MAY, JUN,
    JUL, AUG, SEP, OCT, NOV, DEC
};

int main() {
    enum Months month;
    
    for (month = JAN; month <= DEC; month++) {
        printf("%d ", month);
    }
    
    return 0;
}
```

**Output:**
:::success
**1 2 3 4 5 6 7 8 9 10 11 12**
:::

---
*註: 部分內容截自於網路，此筆記非完全原創。*  
***Latest Updated On：2024.05.31,   
published with the of LICENSE of WTFPL  
Author：Qaron（呱呱）***