# 2018q3 Homework1 contributed by <`AlecJY`> ## 開發工具和規格標準篇 ### 為何不探討 C++ * C++ 是 meta language * 改版很快 * 1999 年 C 和 C++ 分道揚鑣 #### 授權 * glibc -- LGPL * LGPL -- **動態連結**的情況下不會受到License影響 * MIT License -- 不要把作者塗掉就好 ### 第一個 C 編譯器 * 用 ~~property~~ proprietary compiler 編一個較簡單的 c compiler :::danger "property compiler" 是什麼? :notes: jserv ::: ### C99 * 最終版規格書要錢,但是Draft不用 * `&` 在指標要念 address of * `*` value of * [] Array Subscripting Operator * cdecl ### GDB * 改 kernel 很難 * 要用 gdb 記得在 gcc 參數加 ```-c``` * rr 可以把crash錄下來 ## 指標篇 * C 設計給 Unix * 控制記憶體 * pointer type 不能乘除 * L(ocator)value * return type defaults to "int" * 某些硬體架構需要alignmnet * *** 情境 * int \*ptr, value; /\* good \*/ int\* ptr value; /\* bad \*/ "value" type is int * typeof 不是 ISO standard C 的一部份 :::info `typeof` 這個 GNU extension 有什麼作用?能否在 GitHub 找到應用案例並解釋呢? :notes: jserv ::: :::info `typedef`用來幫資料型別取別名 底下這段在 GitHub 找到的程式碼就是利用 `typedef` 處理不同環境下的型別問題 [[來源]](https://github.com/xsoameix/wz/blob/15e6962cf3203402864421fa4e821cd35d5b4fd2/src/type.h#L33) ``` C #if defined(WZ_ARCH_32) typedef signed int wz_intptr_t; typedef unsigned int wz_uintptr_t; #elif defined(WZ_MSVC) typedef signed __int64 wz_intptr_t; typedef unsigned __int64 wz_uintptr_t; #else typedef signed long wz_intptr_t; typedef unsigned long wz_uintptr_t; #endif ``` 只是 `typedef` 應該是 C99 Standard 的一部份,在 6.7.7 節中有說明 `typedef` 詳細的用法,推測老師想要問的是 `typeof` `typeof` 用來取得 type ,在語法上用起來像由 `typedef` 定義出來的 type name 在 GitHub 上找到這段程式碼 [[來源]](https://github.com/Stichting-MINIX-Research-Foundation/minix/blob/03ac74ede908465cc64c671bbd209e761dc765dc/usr.sbin/pwd_mkdb/pwd_mkdb.c#L193) ``` C #define SWAP(sw) \ ((sizeof(sw) == 2 ? (typeof(sw))bswap16((uint16_t)sw) : \ (sizeof(sw) == 4 ? (typeof(sw))bswap32((uint32_t)sw) : \ (sizeof(sw) == 8 ? (typeof(sw))bswap64((uint64_t)sw) : (abort(), 0))))) ``` 這個 Macro 判斷 `sw` 的大小然後對 `sw` 做 byte order 轉換,轉換前先把 `sw` 轉型成 byte order 轉換函式所接受的型別,再將轉換後的值利用 `typeof` 轉型回 `sw` 原本的型別 參考資料 * [C99 Standard](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf) * [Referring to a Type with `typeof`](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html) ::: ## 函式呼叫篇 * parameters 和 local variables 都會佔 stack 空間 * `if (ptr) free(ptr);` => `free(ptr);` * free完就把指標設定成null可以避免double free ## 遞迴呼叫篇 * 遞迴程式不見得會比迭代慢 * 用 clang/llvm 輸出 `-O2` (基本的最佳化) 有些遞迴和迭代轉譯出的組合語言會完全一樣 * 尾端遞迴(tail recursion)做得好,最佳化可以不增加新的系統堆疊,但是不是每個語言編譯器都有支援 * Tail recursion -- 把中間的結果當作參數傳遞,可以避免重複的運算 * 就像是迭代的區域變數放在參數傳遞 * 編譯器在最佳化的時候有可能會改變stack frame的行為 * IEEE 754 的浮點數沒有乘法和加法交換律 :::info 底下是用 JShell 測試 IEEE 754 加法的結果 ``` jshell> 0.1 + 0.2 + 0.3 $1 ==> 0.6000000000000001 jshell> 0.3 + 0.2 + 0.1 $2 ==> 0.6 ``` 第一次聽到 IEEE 754 的乘法和加法沒有交換律覺得很不直覺,仔細想想他的儲存方式後也覺得還好了 ::: ## 前置處理器應用篇 * C11 的 `_Generic` ## goto 和流程控制篇 * `do { ... } while(0)` 避免 dangling else * case 是一個 label * coroutine ## linked list 和非連續性記憶體操作 * ADT(Abstract Data Type) -- 對特定資料結構建立數學模型 ### Circular linked list * 龜兔賽跑演算法 -- 如果有循環,一個快一個慢的情況會相遇 ## 技巧篇 * 傳入指標後直接修改值,比傳入一個 object 再回傳需要多一些檢查 :::info 「[停止使用 strncpy 函數!](http://blog.haipo.me/?p=1065)」的連結已經失效了,所以閱讀 [Wayback Machine 保存的版本](https://web.archive.org/web/20170717113942/http://blog.haipo.me/?p=1065) * 如果 src 的長度小於 n , dest 剩下的位置會補 0 * src 的長度大於 n 的話, dest的結尾不會補 0 * strdup 會用 malloc 分配足夠的空間來放置複製的字串,只是使用完要記得 free * strdupa 將複製的字串放在 stack ,所以離開函式後就會釋放空間 :::