# 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(呱呱)***