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