# 為什麼要深入學習 C 語言? ###### tags: `sysprog2018` Contributed by <[`aben20807`](https://github.com/aben20807)> :::success 「徹底瞭解自己」 ::: 若不知底層運作、背後原理,那就只能當個使用者而非開發者 ## 開發工具和規格標準 + C、C++ 已經是不同的語言,部份功能 C++ 不能相容 C e.g. C99 designated initializers ~[27:46]~ + C 語言是 WYSIWYG ~[18:18]~:高階的組合語言,可以較輕易的對應組合語言、二進位 ### Object + 佔有空間的表示法 + 延伸:struct 可利用長度為零的陣列來達到動態調整大小,C99 有 flexible array member [[source - 6.17 Arrays of Length Zero]](https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html) + 實驗: ```c #include <stdio.h> typedef struct {int x; int arr[];} Foo; // flexible array member 不可單獨存在 typedef struct {int x; int arr[0];} Bar; int main(){ printf("sizeof(Foo):\t%ld\n", sizeof(Foo)); static Foo f = {1, {2, 3}}; // 必須 static printf("sizeof(f):\t%ld\n", sizeof(f)); printf("f.x:\t\t%d\n", f.x); printf("f.arr[0]:\t%d\n", f.arr[0]); printf("f.arr[1]:\t%d\n", f.arr[1]); printf("-------------------\n"); printf("sizeof(Bar):\t%ld\n", sizeof(Bar)); Bar b = {7, {8, 9}}; // warning: excess elements in array initializer printf("sizeof(b):\t%ld\n", sizeof(b)); printf("b.x:\t\t%d\n", b.x); printf("b.arr[0]:\t%d\n", b.arr[0]); printf("b.arr[1]:\t%d\n", b.arr[1]); return 0; } ``` 結果如下 + 發現 flexible array members 能真的存到變動長度的值,但是 size 竟然不變 + 不變的原因是因為 incomplete type 所以 sizeof 無作用 + b 的 size 也不變,是因為 arr[0] 的 size 就是 0 ``` sizeof(Foo): 4 sizeof(f): 4 f.x: 1 f.arr[0]: 2 f.arr[1]: 3 ------------------- sizeof(Bar): 4 sizeof(b): 4 b.x: 7 b.arr[0]: 1707756032 b.arr[1]: -809474503 ``` + zero-length arrays 可編譯且無警告的用法如下: ```c Bar *ptrb = (Bar *)malloc(sizeof(Bar) + 2 * sizeof(int)); ptrb->arr[0] = 8; ptrb->arr[1] = 9; printf("ptrb->arr[0]:\t%d\n", ptrb->arr[0]); // 8 printf("ptrb->arr[1]:\t%d\n", ptrb->arr[1]); // 9 ``` + 既然上面方法可以,那測試不配置空間就直接用 `.` 來賦值給成員 + 可以印出結果,但是發生 <span style="color: red;">*** stack smashing detected ***: &lt;unknown&gt; terminated</span> + 這是 gcc 的保護機制:存取到非法的 stack 空間,因此此種寫法相當危險 ```c Bar ano_b = {77}; ano_b.arr[0] = 88; // 非法存取! ano_b.arr[1] = 99; // 非法存取! printf("ano_b.arr[0]:\t%d\n", ano_b.arr[0]); // 88 // 非法存取! printf("ano_b.arr[1]:\t%d\n", ano_b.arr[1]); // 99 // 非法存取! ``` + 延伸其他討論 + flexible array members 不一定要 static 賦值,也可以像 zero-length arrays 利用 malloc 動態配置 + zero-length arrays 用 static 賦值 (有沒有用 static 都會警告) 時 arr 的兩個元素會初始化為 0 (不加 static 會是亂數) + 小整理 ||flexible array members|zero-length arrays| |:-:|:-:|:-:| |syntax|arr[]|arr[0]| |standard|ISO C99|ISO C90 (GNU C extension)| |使用位置限制|限於 struct 的最後一個成員且不能唯一|無| |空間分配|static initialization or malloc()|malloc()| |e.g.|[struct f_midi](https://github.com/torvalds/linux/blob/6f0d349d922ba44e4348a17a78ea51b7135965b1/drivers/usb/gadget/function/f_midi.c#L102)|[struct ip6t_entry](https://github.com/torvalds/linux/blob/6f0d349d922ba44e4348a17a78ea51b7135965b1/include/uapi/linux/netfilter_ipv6/ip6_tables.h#L128)| ### 語法糖 (Syntactic sugar) + `a[i]` 等價於 `*(a + i)` (限於 expression,不可用於宣告,但函式的 parameter 宣告又可等價) + `a->x` 等價於 `(*a).x` ### lvalue §6.3.2.1 + locator value + 較好懂的解讀 + 具有空間可以存放數值,上面提到 object 是佔有空間的表示 + 通常會用 identifier 來代表儲存位置,可透過 dereference 得到真正的記憶體位址 + 可以出現在 `=` 的左右邊 + 又細分有 modifiable lvalue + 非 array type 、非 incomplete type 、無 `const` 修飾 ### rvalue + 存放在記憶體中的資料,不可賦值,所以只能出現在 `=` 右方 + 另外 `&` 運算子的操作也不行,因為其需要 lvalue 的運算元 :::info ### lvalue v.s. rvalue 修過 compiler 可能會比較知道,一個 language 可能有很多種 type 而 token 一開始在分析時就可以知道是整數、浮點數、字串、變數... 其中變數 (identifier) 會再有一個空間去存放 value 並記錄在 symbol table 中 這就導致了變數可以賦值,但是其他型別不行,也就是這裡的 lvalue 變數與其他型別經過四則混合運算後會變成一個數值,"通常"就不是一個變數,而是其他型別,也就是 rvalue + e.g. ```c int x; x = 8; // 型別為 int 的 object x 佔有空間並存放常數 8 int y = x; // x 可以出現在 `=` 右邊 3 = 8; // err: 3 是常數,非 lvalue (x + 1) = 8; // err: (x + 1) 已經不是 lvalue const int z = 7; // 是 lvalue 但不可改寫 (not a modifiable lvalue) z = 9; // err: assignment of read-only variable 'z' ``` p.s. 實際 C 語言會比較複雜 + 例如 dereference 可以用在 rvalue 上並獲得 lvalue e.g. `*(ptr + 1)` ::: ### pointer 運算 §6.5.3.2 ~[1:21:00]~ + \+ 、\- 、\~ + 並不是位址加 1,而是會依照 pointer 本身指向的 type + 實驗:(\~)☢️ scanf %s 可加可不加 &☢️ ### WCHAR ~[1:25:18]~ + 長度非固定 + 有特別的 w 系列函式 + 實驗:(中文字)☢️ ### sizeof + `sizeof(struct A)` `sizeof(a)` ### incomplete type §6.2.5.22 ~[1:40:00]~ ### NaN ~[2:01:00]~ + 除以 0 + INF ### const 位置 ~[2:11:00]~☢️ ### GDB ~[2:19:00]~ + 不同硬體架構、不同程式語言 + 模擬器:模擬指令的執行 + dwarf + -g v.s. -g3☢️ + 前置處理器應用篇 (2016-06-29) ~[1:26:30]~