--- title: 'Struct、Union、Enum' disqus: kyleAlien --- Struct、Union、Enum === ## OverView of Content 如有引用參考請詳註出處,感謝 :smile: :::success * 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/) 本篇文章對應的是 [**C 語言中的 Struct 定義、初始化 | 對齊、大小端 | Union、Enum**](https://devtechascendancy.com/c-struct_alignment_endianness_union_enum/) ::: [TOC] ## struct 結構概念 > 整合變數的一個關鍵字 struct,用於變數、引數都十分的方便 結構主要的目的是在 **++整合參數++**,以往在全域需告變數時,必須分開去操控,難以記憶其關鍵字,而**使用 struct 就可以整合所有相關的變數** 1. 方便於管理 2. 方便使用 ### struct 結構 - 定義 * 通常來說我們會把有相關的變數使用結構一併管理,使用 struct 關鍵字 + 大括號 {} :::warning **結構的 ++型態定義++ 並不佔有記憶體空間** ::: ```c= // 分開宣告 char name[]; int age; long id; // 使用 struct 整合相關變數 struct Info_T { char *name; int age; long id; }; ``` ### struct 結構 - 訪問 * **struct 結構的訪問方式有兩種** 1. ^1.^ 結構的變數使用符號 `.` 來訪問成員;^2.^ 結構的指標 (pointer)則使用 `->` 訪問 ```c= void visit_struct() { // 結構的變數 struct Info_T info; info.name = "Sean"; info.age = 28; info.id = 123132; printf("name: %s\n", info.name); printf("age: %d\n", info.age); printf("id: %d\n\n", info.id); // 結構的指標 struct Info_T *pinfo = &info; printf("Ptr name: %s\n", pinfo->name); printf("Ptr age: %d\n", pinfo->age); printf("Ptr id: %d\n", pinfo->id); } ``` > ![](https://i.imgur.com/wCmZor1.png) 2. 手動使用偏移量計算,並調整指標的位置來訪問 ```c= void visit_struct_by_ptr() { struct Info_T info; info.name = "Sean"; info.age = 28; info.id = 123132; printf("&name: %p\n", &info.name); printf("&age: %p\n", &info.age); printf("&id: %p\n\n", &info.id); // 轉換為結構的第一個地址 char *p = (char *) &info; *p = "Alien"; *(p + sizeof(char*)) = 10; // 加上 char* 指針大小的位移(篇一到 age 變數) // 加上 char* 跟 int 大小的位移(偏移到 id 變數) // 並同時將指標強制轉型為 int* ,才能賦予 int 類型的數值 *((int*) (p + sizeof(char*) + sizeof(int))) = 998877; printf("p: %p, name: %s\n", p, info.name); printf("p: %p, age: %d\n", (p + sizeof(char*)), info.age); printf("p: %p, id: %ld\n\n", (p + sizeof(char*) + sizeof(int)), info.id); } ``` > ![](https://i.imgur.com/Dd7appo.png) ### struct 結構 - 宣告、定義 * **宣告出來後就在記憶體內佔有空間** * 在上方我們宣告了 `Info_T` 這個結構,在**呼叫時就要使用全名呼叫(也就是要包含 `struct` 關鍵字)** :::info * 完整的型態為 `struct Info_T`,而要使用該型態時就必須全型態名 ```c= // 型態宣告 struct Info_T { char *name; int age; long id; }; // 變數定義 struct Info_T info1; ``` ::: ```c= #include <stdio.h> #include <stdlib.h> struct Info_T { char *name; int age; long id; }; // 接收傳值的 Function void printInfo(struct Info_T info) { printf("name: %s\n", info.name); printf("age: %i\n", info.age); printf("id: %li\n\n", info.id); } int main(void) { // 定義兩個結構變量 struct Info_T info1, info2; // 使用 '.' 指定定義結構內容 info1.name = "Alien"; info1.age = 20; info1.id = 9627; // 傳值呼叫 Function printInfo(info1); info2.name = "Pan"; info2.age = 17; info2.id = 33; printInfo(info2); return EXIT_SUCCESS; } ``` **--實作結果--** > ![](https://i.imgur.com/dlgd3Ju.png) ### struct 結構宣告 - 同時定義變數 * 可以在定義的同時一起宣告出結構變數;變數方至於 大括號 `{}` 之後,並可宣告多個變數 ```c= #include <stdio.h> #include <stdlib.h> struct Info_T { char *name; int age; long id; } Default_1, Default_2; // 定義兩個變數 void printInfo(struct Info_T info) { printf("name: %s\n", info.name); printf("age: %i\n", info.age); printf("id: %li\n\n", info.id); } int main(void) { Default_1.name = "Hello"; Default_1.age = 12; Default_1.id = 123; printInfo(Default_1); Default_2.name = "World"; Default_2.age = 21; Default_2.id = 321; printInfo(Default_2); return EXIT_SUCCESS; } ``` **--實作結果--** > ![](https://i.imgur.com/zrUQhgP.png) ### struct 簡化宣告 - define、typedef * 以往必須使用 struct 全結構才可進行宣告變數的動作,以下有兩種方法 `#define`、`typedef` 可以簡化該行為 :::warning * `#define`、`typedef` 兩者是有差別的,`#define` 是在預編譯期間做替換,而 `typedef` 則是在編譯期間定義出新類型 ::: 1. **使用宏定義關鍵自 `#define`**:其格式為 `#define <新名稱> <struct 原先名稱>` :::info * 如果使用分開定義那要特別注意,C 是順序式編程(命令式設計注重程式的順序),要把簡化定義寫在下方 ::: ```c= #include <stdio.h> #include <stdlib.h> // #define Info struct Info_T 不可寫在上方,因為還未定義 struct Info_T{ char *name; int age; long id; }; // 簡化定義寫在下方 // 將結構 `struct Info_T` 定義為 `Info` #define Info struct Info_T // 引數使用 Info 簡化 void printInfo(Info info) { printf("name: %s\n", info.name); printf("age: %i\n", info.age); printf("id: %li\n\n", info.id); } int main(void) { // 宣告使用 Info 簡化 Info info; info.name = "Alien"; info.age = 21; info.id = 9528; printInfo(info); return EXIT_SUCCESS; } ``` 2. **使用類型關鍵字 `typedef` 定義**:其格式與 `#define` 相反,格式如 `#define <struct 原先名稱> <新名稱>` (記得使用分號 `;` 結尾) ```c= #include <stdio.h> #include <stdlib.h> struct Info_T{ char *name; int age; long id; }; // 定義新類型 typedef struct Info_T Info; // 引數使用 Info 簡化 void printInfo(Info info) { printf("name: %s\n", info.name); printf("age: %i\n", info.age); printf("id: %li\n\n", info.id); } int main(void) { // 宣告使用 Info 簡化 Info info; info.name = "Alien"; info.age = 21; info.id = 9528; printInfo(info); return EXIT_SUCCESS; } ``` 如果覺得分開宣告很麻煩的話,也可以將 `struct`、`typedef` 兩個關鍵字合併使用,範例如下 ```c= #include <stdio.h> #include <stdlib.h> // struct`、`typedef` 兩個關鍵字合併使用 // 定義出一個「新類型 Info」,並且該類型為 struct typedef struct Info_T { char *name; int age; long id; } Info; // 引數使用 Info 簡化 void printInfo(Info info) { printf("name: %s\n", info.name); printf("age: %i\n", info.age); printf("id: %li\n\n", info.id); } int main(void) { // 宣告使用 Info 簡化 Info info; info.name = "Alien"; info.age = 21; info.id = 9528; printInfo(info); return EXIT_SUCCESS; } ``` ## struct 結構 - 初始化 struct 結構初始化有分為多種方式,接下來要介紹的就是不同的結構初始化的方案(以下是針對靜態初始化的結構做介紹) ### 結構順序初始化 - 預設初始化 * 預設初始化是 **手動 ==按照順序==** 對其結構內的變數進行初始化 ```c= #include <stdio.h> struct Info { int age; int height; int weight; }; void printInfo(struct Info *i) { printf("age: %d\n", i->age); printf("height: %d\n", i->height); printf("weight: %d\n", i->weight); } int main() { // 順序初始化 struct Info myInfo_1 = { 25, 170, 65 }; printInfo(&myInfo_1); printf("Hello World"); return 0; } ``` **--實作--** > ![](https://i.imgur.com/gjS6wHE.png) ### 結構 - 指定、複合初始化 :::warning * 使用 **指定、複合文字初始化時** 時要==注意==,當**沒有被指定到的值會初始化為 0** * **指定初始化有 C 版本的限制**:**==C99 之後== 才能指定參數初始化**,像是 `C90`、`C85` 等等都不可指定初始化 ::: * **結構指定初始化**: ```c= #include <stdio.h> struct Info { int age; int height; int weight; }; void printInfo(struct Info *i) { printf("age: %d\n", i->age); printf("height: %d\n", i->height); printf("weight: %d\n", i->weight); } int main() { struct Info myInfo_1 = { // 不按照順序初始化,也可以選定初始化 .weight = 65, .height = 170 }; printInfo(&myInfo_1); return 0; } ``` **--實作--** > ![](https://i.imgur.com/JE4OE5w.png) * **結構複合初始化**: ```c= #include <stdio.h> struct Info { int age; int height; int weight; }; void printInfo(struct Info *i) { printf("age: %d\n", i->age); printf("height: %d\n", i->height); printf("weight: %d\n", i->weight); } int main() { struct Info myInfo_1 = {17, 168, 55}; //"1. " myInfo_1.age = 20; myInfo_1.height = 168; myInfo_1.weight = 58; //"2. " 結構複合初始化 myInfo_1 = (struct Info) {.weight = 60, .age = 20, .height = 168}; printInfo(&myInfo_1); //"3. " 結構複合初始化 myInfo_1 = (struct Info) {.weight = 60, .height = 168}; printInfo(&myInfo_1); return 0; } ``` 1. 一般在指定結構的值實必須分別指定 2. 可以透過==複合文字==一次指定全部,**不必按照順序,可特別指定** 3. 該範例使用 複合文字初始化 但沒有指定到 age,該 age 即初始化為 0 > ![](https://i.imgur.com/nNg7iM2.png) ## struct 結構 & 函數 使用結構型態可以**簡化函數的引數數量** ```c= // 舊方法 void printInfo_1(char* str, int age, int id) { //... TODO } // 使用 struct 類型 void printInfo_2(struct Info_T info) { //... TODO } ``` :::info * **函數的原型宣告** > 函數的原型宣告中的引數(參數)可以省去 ```c= // 原型宣告 - 保持引數 void printInfo_2(struct Info_T info); // 原型宣告 - 省去引數 void printInfo_3(struct Info_T); ``` ::: ### 函數回傳結構 - 變數、指標 * 函數的返回值假設要回傳結構,可以有兩種方案:^1.^ 用結構的型態回傳整組變數,也可以 ^2.^ 回傳結構的指標 ```c= #include <stdio.h> #include <stdlib.h> typedef struct Info_T{ char *name; int age; long id; }Info; // 引數使用 Info 簡化 Info FixInfo(Info info) { info.id = 9123; info.age += 1; return info; } void printInfo(struct Info_T info) { printf("name: %s\n", info.name); printf("age: %i\n", info.age); printf("id: %li\n\n", info.id); } int main(void) { // 宣告使用 Info 簡化 Info info; info.name = "Alien"; info.age = 21; info.id = 9528; info = FixInfo(info); printInfo(info); return EXIT_SUCCESS; } ``` > ![](https://i.imgur.com/IBc0CHB.png) ## struct 實現注意 在電腦中 **struct 在記憶體中的排列方式,以及佔用空間** 對於我們了解計算機底層十分重要,以下章節將說明「資料對齊」跟「結構」的關係 ### 自動對齊 * 在 struct 中宣告其變數,在**記憶體中的排版式固定的 (這取決於編譯器),這個概念由如 JVM 對象創建時設定的對象頭,其中的 Padding 對齊 (方便於總線讀取數據)** :::info * **對齊訪問的特點**? 對齊訪問是「**犧牲空間來換取效率**」,而非對齊則相反,效率低不過空間消耗也相對低 * **對齊的數**? 這會有關到硬體的數據總線的多寡;如果是 32 條,就是一次讀取 32 bit 的數據(對齊 32),64 則知一次 64 bit 數據(對齊 64) ::: ```c= #include <stdio.h> struct { float a; int b; char c; float d; } Test; int main() { //"1. " printf("float size %d byte\n", sizeof Test.a); printf("int size %d byte\n", sizeof Test.b); printf("char size %d byte\n", sizeof Test.c); printf("float size %d byte\n", sizeof Test.d); //"2. " printf("a address: %p\n", &Test.a); printf("b address: %p\n", &Test.b); printf("c address: %p\n", &Test.c); printf("d address: %p\n", &Test.d); return 0; } ``` 1. 用 sizeof 算出的大小雖然是正確,char 是 1 Byte 2. 取 Addr 出來看 char 卻占了 4 個 byte(這就是編譯器幫我們自動對齊的展現) **--實做--** > 從實際抓取到的參數值,**它的地址是以 ++4 為倍數++ 編排** > > ![](https://i.imgur.com/qpvmEVd.png) ### 手動對齊 - pragma pack :::warning 如果不是特殊需求建議少用(`pragma pack`) ::: * 在前面我們可以看到編譯器自動幫我們實現的對齊;有自動對齊當然就有 **手動對齊**,**透過 `#pragma pack([對齊 Byte 數量])` 關鍵字設定對齊** :::info * 定義的格式:**`#pragma pack()` 開頭、`#pragma pack()` 結尾** ::: 1. **一字節 (1 Byte) 對齊** 範例如下 ```c= #pragma pack(1) struct Class { int BookCount; short chairCount; char neckName[11]; }; void align_by_pragma_pack() { printf("Class struct size: %d\n", sizeof(struct Class)); } #pragma pack() // 注意需要結尾 ``` > 從結果來看,它可以適時減少空間的消耗 > > ![](https://i.imgur.com/x0aojZM.png) 2. **兩字節 (2 Byte) 對齊** 範例如下 ```c= #pragma pack(2) // 修改為 2 struct Class { int BookCount; short chairCount; char neckName[11]; }; void align_by_pragma_pack() { printf("Class struct size: %d\n", sizeof(struct Class)); } #pragma pack() // 注意需要結尾 ``` > ![](https://i.imgur.com/vSggI2o.png) 3. **兩字節 (4 Byte) 對齊** ```c= #pragma pack(4) // 修改為 4 struct Class { int BookCount; short chairCount; char neckName[11]; }; void align_by_pragma_pack() { printf("Class struct size: %d\n", sizeof(struct Class)); } #pragma pack() // 注意需要結尾 ``` > ![](https://i.imgur.com/g5beBV9.png) ### GCC 手動對齊 - `__attribute__` * GCC 手動對齊的方式有兩種:使用時直接放置在結構(`struct`)的後方即可 * **`__attribute__((packed))`**:**其中 `packed` 是代表取消對齊** * `__attribute__((aligned(n)))`:**`aligned(n)` 代表元素要對齊的大小** 1. **使用 `__attribute__` 取消編譯器的對齊** ```c= #include <stdio.h> struct MyClass { int BookCount; short chairCount; char neckName[11]; }__attribute__((packed)); void packed_by_gcc() { printf("packed struct size: %d\n", sizeof(struct MyClass)); } ``` > ![](https://i.imgur.com/NOyFykk.png) 2. 1024 對齊:這裡我們看兩個情況 * 結構大小 **不超過 1024**:表示編譯時元素須在 1024 內對齊 ```c= #include <stdio.h> typedef struct { int BookCount; short chairCount; char neckName[11]; }__attribute__((aligned(1024))) MyClass_2; void aligned_by_gcc() { printf("packed struct size: %d\n", sizeof(MyClass_2)); } ``` > ![](https://i.imgur.com/24hBcs3.png) * 結構大小 **超過 1024**:超過 1024 則新增一個對齊大小 ```c= #include <stdio.h> typedef struct { int BookCount; short chairCount; char neckName[1025]; // 故意超出 1024 的大小 }__attribute__((aligned(1024))) MyClass_2; void aligned_by_gcc() { printf("packed struct size: %d\n", sizeof(MyClass_2)); } ``` > ![](https://i.imgur.com/pMyBx7v.png) ## struct 數據結構 ### struct & Array * **結構陣列**:可以定義、初始化多個結構形成的陣列,範例如下 ```c= #include <stdio.h> #include <stdlib.h> typedef struct Info_T{ char *name; int age; long id; } Info; void printInfo(struct Info_T info) { printf("name: %s\n", info.name); printf("age: %i\n", info.age); printf("id: %li\n\n", info.id); } int main(void) { // 定義結構陣列,並透過順序初始化! Info infos[3] = { {"Alien", 21, 1111}, {"Pan", 13, 2222}, {"Kyle", 15, 3333}, }; printInfo(infos[0]); printInfo(infos[1]); printInfo(infos[2]); return EXIT_SUCCESS; } ``` > ![reference link](https://i.imgur.com/5ZGRoRx.png) ### struct & Linked * 可以使用 struct 來製作一個串列 Linked,這樣就可以達到一種 **數據結構的 `LinkedList`**,以下使用指標來指向下一個位子,範例如下 ```c= #include <stdio.h> #include <stdlib.h> typedef struct Info_T { char name[5]; int age; long id; // 下一個 struct Info_T *next; }Info; // Reference 引數 void printInfo(struct Info_T* info) { printf("name: %s\n", info->name); printf("age: %i\n", info->age); printf("id: %li\n\n", info->id); } int main(void) { Info info1 = {"Alien", 18, 111, NULL}; Info info2 = {"Pan", 20, 222, NULL}; Info info3 = {"Kyle", 12, 333, NULL}; info1.next = &info2; info2.next = &info3; Info* temp = &info1; while(temp != NULL) { printInfo(temp); // 使用指標取值必須使用 '->' 符號(優先級問題),不然就不需使用 (*temp) temp = temp->next; } return EXIT_SUCCESS; } ``` **--實作--** > ![](https://i.imgur.com/8SmpOj0.png) ## Union 中文又稱為 **聯合體**、**共用體**;簡單來說就是 Union 內部成員是共用空間 ### Union 使用、特性 * Union 所有成員 **共享空間** ```c= union MyUnion_T { int a; short b; char c; }; void base_use_union() { union MyUnion_T u; u.a = 0x0F000F0F; printf("a value: %d\n", u.a); // 還在 int 範圍內,全部顯示 printf("b value: %d\n", u.b); // 超過 short 範圍,顯示 0x0F0F 的數值 printf("c value: %d\n", u.c); // 超過 char 範圍,顯示 0x0F 的數值 } ``` > ![](https://i.imgur.com/h4zRxLw.png) * **不存在對齊問題**,因為 **Union 就是一個空間**,都是從同一個地址開始 ```c= union MyUnion_T { int a; short b; char c; }; void union_addr() { union MyUnion_T u; printf("a addr: %p\n", &u.a); printf("b addr: %p\n", &u.b); printf("c addr: %p\n", &u.c); } ``` 所有成員都是相同的起始地址 > ![](https://i.imgur.com/5NiQTnY.png) * **Union 其實功能跟使用指標強制轉型一樣**,透過強制轉型來讓它使用不同的格式分析指標;接下來 **使用指標的轉型來展現 Union 的功能** ```c= void ptr_replace_union() { union MyUnion_T u; u.a = 0x0F000F01; char *p = (char*) &u; printf("u addr: %p, p addr: %p\n", &u, p); printf("ptr a value: %d\n", *((int*) p)); printf("ptr b value: %d\n", *((short*) p)); printf("ptr c value: %d\n", *((char*) p)); } ``` > ![](https://i.imgur.com/n1gb8Lg.png) ### 大端小端概念 :::info 大、小端這原本是出現在小說中的說詞,後來才用到電腦中表達序列化規則 ::: * 在串列序列化時會碰到傳送數據的順序問題,回到最基礎的傳送 int 類型,假設傳送一個 int 類型的數據是 4 Byte,那就分為兩種情況 (請看下圖 1. 假設有 Byte 0 ~ 3,「高 Byte」對「高地址」,就稱為 **小端**(對電腦讀取友善) > ![](https://i.imgur.com/lIrwFg4.png) 2. 假設有 Byte 3 ~ 0,「高 Byte」對「低地址」,就稱為 **大端**(對人類讀取友善) > ![](https://i.imgur.com/NHMC6En.png) * 以下是 `int i = 0xF0`,也就是 **`0x000000F0` 在大小端中的分布**;**分部是以 Byte 為單位**,低位元為 `0xF0`,高位元為 `0x00` * **小端的 Byte 分佈**: > ![](https://i.imgur.com/krPSbgK.png) * **大端的 Byte 分佈**: > ![](https://i.imgur.com/Grz6f4Q.png) ### Union 判斷大小端 * **使用 Union 就可以測定大小端** :::info * 數據從記憶體低位置開始存,存的順序才由大小端決定 ::: ```c= union Endian_t { int iVal; char cVal; }; int is_little_endian(void) { int val = 0x00000001; // 0x00 is height, 0x01 is low union Endian_t t; t.iVal = val; if(t.cVal == 0x01) { // little_endian return 1; } return 0; } ``` > 低地址存低位元就可以讀出數據 > > ![](https://i.imgur.com/JgAvNtx.png) * 同樣可以使用指標判定大小端 ```c= int is_little_endian_ptr(void) { int val = 0x00000001; // 0x00 is height, 0x01 is low char* p = (char*) &val; if(*p == 0x01) { // little_endian return 1; } return 0; } ``` > 低地址存高位元就則無法讀出數據 > ## Enum 枚舉 Enum 枚舉,定義一個符號,並且該符號與 **常量綁定** ### Enum 使用 1. **Enum 數值當常量基礎使用**:enum 可指定其數值(可正可負);如果**在中途切換為別的變數,也會從那個變數開始加**(自動會往後加) ```c= #include <stdio.h> enum Test_T { A = 0, B, C, D = 1, E, F, G = -1, H }; void base_enum() { printf("A: %i\n", A); printf("B: %i\n", B); printf("C: %i\n", C); printf("D: %i\n", D); printf("E: %i\n", E); printf("F: %i\n", F); printf("G: %i\n", G); printf("H: %i\n", H); } ``` > ![](https://i.imgur.com/EeEBgoV.png) 2. **分開定義類型、變量** ```c= enum week { SUN, MON, TUE, WEN, THU, FRI, SAT }; void base_use_2() { enum week w; } ``` 3. **使用 `typedef` 定義新類型** ```c= typedef enum { SUN_2, MON_2, TUE_2, WEN_2, THU_2, FRI_2, SAT_2 } week_t; void base_use_3() { week_t w; } ``` :::danger * 由於 Enum 是全局常量,所以一個文件中不可以重複定義 > ![](https://i.imgur.com/L9G3KoL.png) ::: ## 更多的 C 語言相關文章 關於 C 語言的應用、研究其實涉及的層面也很廣闊,但主要是有關於到系統層面的應用(所以 C 語言又稱之為系統語言),為了避免文章過長導致混淆重點,所以將文章係分成如下章節來幫助讀者更好地從不同的層面去學習 C 語言 ### C 語言基礎 * **C 語言基礎**:有關於到 C 語言的「語言基礎、細節」 :::info * [**理解C語言中的位元操作:位元運算基礎與宏定義**](https://devtechascendancy.com/bitwise-operations-and-macros-in-c/) * [**C 語言解析:void 意義、NULL 意義 | main 函數調用、函數返回值意義 | 臨時變量的產生**](https://devtechascendancy.com/meaning_void_null_return-value_temp-vars/) * [**C 語言中的 Struct 定義、初始化 | 對齊、大小端 | Union、Enum**](https://devtechascendancy.com/c-struct_alignment_endianness_union_enum/) * [**C 語言儲存類別、作用域 | 修飾語、生命週期 | 連結屬性**](https://devtechascendancy.com/c-storage-scope-modifiers-lifecycle-linkage/) * [**指標 & Array & typedef | 指標應用的關鍵 9 點 | 指標應用、細節**](https://devtechascendancy.com/pointers-arrays-const-typedef-sizeof-null/) ::: ### 編譯器、系統開念 * **編譯器、系統開念**:是學習完 C 語言的基礎(或是有一定的程度)之後,從編譯器以及系統的角度重新檢視 C 語言的一些細節 :::warning * [**理解電腦記憶體管理 | 深入瞭解記憶體 | C 語言程式與記憶體**](https://devtechascendancy.com/computer-memory_manager-c-explained/) * [**C 語言記憶體區塊規劃 | Segment 段 | 字符串特性**](https://devtechascendancy.com/c-memory-segmentation-string-properties/) * [**編譯器的角度看程式 | 低階與高階、作業系統、編譯器、直譯器、預處理 | C語言函數探討**](https://devtechascendancy.com/compiler-programming-os-c-functions/) ::: ### C 語言與系統開發 * **C 語言與系統開發**:在這裡會說明 C 語言的實際應用,以及系統為 C 語言所提供的一些函數、庫... 等等工具,看它們是如何實現、應用 :::danger * [**了解 C 語言函式庫 | 靜態、動態函式庫 | 使用與編譯 | Library 庫知識**](https://devtechascendancy.com/understanding-c-library-static-dynamic/) * [**Linux 宏拓展 | offsetof、container_of 宏、鏈表 | 使用與分析**](https://devtechascendancy.com/linux-macro_offsetof_containerof_list/) ::: ## Appendix & FAQ :::info ::: ###### tags: `C`