# 2018q3 Homework1 contributed by <`dvsseed`> 參考書目: [Computer Systems: A Programmer's Perspective, 3/E](http://csapp.cs.cmu.edu/) --- [TOC] --- # 為什麼要深入學習 C 語言? 如同老師一開始所闡明的真理~ **回歸第一手資料([ISO/IEC 9899 C 語言規格](http://www.open-std.org/jtc1/sc22/wg14/www/docs/C99RationaleV5.10.pdf)),透過反思 C 語言程式設計的細節,重新學習電腦原理** >要屏棄自己過往臆測為主的學習方式,探究實際 C 程式的行為並反思箇中原理,並正視自己的盲點,從而打下穩固的基礎。[name=曾令燊][time=Wed, Sep 19, 2018 1:53 PM][color=#d163f9] ## 你所不知道的 C 語言 ### 為什麼要深入學習 C 語言? * 老師提到的 c++ andy c++rush,如下: ```clike= int main() { [](){}; []{}(); {}[]{}; } ``` - 我利用 gcc 編譯後出現錯誤[color=#ff0000],如下: ```c c++andy_c++rush.c: In function ‘main’: c++andy_c++rush.c:5:5: error: expected expression before ‘[’ token [](){}; ^ c++andy_c++rush.c:6:5: error: expected expression before ‘[’ token []{}(); ^ c++andy_c++rush.c:7:7: error: expected expression before ‘[’ token {}[]{}; ^ ``` :::info 需要加上編譯參數,如 `-std=c++11`,明確指定 C++ 標準的版本 (你可以順便思考,為何編譯器不預設都用可支援的最新語言規格?) :notes: jserv ::: > **謝謝老師的指導!!** > 經重新編譯,如下 > ```shell > $ g++ -std=c++11 -Wall -o candycrush c++andy_c++rush.c > ``` > 終於成功!! > > 另外,針對老師的問題,搜尋 man g++ 如下 > ```shell > $ man g++ | grep -B l -e '-std.* default' > gnu++1y > GNU dialect of -std=c++14. This is the default for C++ code. > ``` > 猜想~GNU預設都是以支援最新定案為主--`c++14`?! > [name=曾令燊] - ISO/IEC 9899 (簡稱 “C99”) 規格書 ([PDF](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf)),重點在於 “everything is object” > 要下功夫去精讀這本聖經規格工具書~552 pages > [name=曾令燊] - 安裝 cdecl 程式 ```shell $ sudo apt-get install cdecl ``` > 利用 `cdecl` 可瞭解c語法意思及產生c語法 > [name=曾令燊] - 學習 GDB > 利用 `cdecl` 可瞭解c語法意思及產生c語法 > [name=曾令燊] - 安裝 rr (Record and Replay Framework) ```shell $ cd /tmp $ wget https://github.com/mozilla/rr/releases/download/5.2.0/rr-5.2.0-Linux-$(uname -m).deb $ sudo dpkg -i rr-5.2.0-Linux-$(uname -m).deb ``` - 安裝 Vim 及 Visual Studio Code > 實際安裝後發現其UI操作不錯用,可取代`Sublime Text`, > 連常用的 `columns select` 也有...如下: > **In Visual Studio Code version 1.0, you can now select columns by holding down `Shift + Alt`, then click and drag with the mouse.** > [name=曾令燊] --- ### 你所不知道的 C 語言: 指標篇 * 這個講座並非「頭腦體操」 本篇一開頭~取自 [C Traps and Pitfalls](http://www.literateprogramming.com/ctraps.pdf) 的案例 “Understanding Declarations” ```clike (*(void(*)())0)(); ``` 使用 [godbolt](http://gcc.godbolt.org/) 線上輸入程式碼並加上Assembly output `-Os` (空間最佳化)如下圖![Code Editor](https://i.imgur.com/sFTuTAr.png) 科技公司面試題: ```clike void **(*d) (int &, char **(*)(char *, char **)); ``` 我想到老師說過的工具 `cdecl` 如下 ```shell $ cdecl Type `help' or `?' for help cdecl> explain void **(*d) (int &, char **(*)(char *, char **)); Warning: Unsupported in C -- 'reference' declare d as pointer to function (reference to int, pointer to function (pointer to char, pointer to pointer to char) returning pointer to pointer to char) returning pointer to pointer to void cdecl> ``` - 先羅列你已經知道的部分 - void * 之謎 - 要注意 undefined behavior (UB) 問題 - 沒有「雙指標」只有「指標的指標」 - 實作老師的範例C碼後,瞭解了 ** 與 * 的差異,另外,我的理解圖想像如下 ==$[ptrA]\Rightarrow[*ptrA|\&A]\rightarrow[A|1]$ $[ptrA]\Rightarrow[**p]\Rightarrow[*p|\&B]\rightarrow[B|2]$== - array 與 pointer 可互換中,我改了程式碼,如下 ```c #include <stdio.h> int main() { int x[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; printf("%d\n", x[ x[ 2 + 2 ] ]); } ``` - 在 GDB 實作時,遇到 ==error==,如下 ```shell (gdb) list 1 int main() { 2 int a[3]; 3 struct { double v[3]; double length; } b[17]; 4 int calendar[12][31]; 5 } 6 (gdb) b main Breakpoint 1 at 0x675: file test2.c, line 1. (gdb) r Starting program: /home/davis/homework1/test2 Breakpoint 1, main () at test2.c:1 1 int main() { (gdb) p memcpy(calendar, b, sizeof(b[0])) 'memcpy' has unknown return type; cast the call to its declared return type ``` > 給 `memcpy` 一個 `return` 型態就可以。用 `p (void *) memcpy(calendar, b, sizeof(b[0]))` 可以解決。 by @birdca - Null pointer? - 有關程式,如下 ```c #include <stddef.h> int main() { return (NULL == 0); } ``` 業經 `gcc` 及 `./null` 執行後...結果是 `1(非零)` --- ### 你所不知道的 C 語言: 函式呼叫篇 C 語言不允許 nested function,這件事簡化了 C 編譯器的設計... * 再論 Function - 在 C 語言中,“function” 其實是退化過的形式 - Process 和 C 程式的關聯 - [Done Is Better Than Perfect](http://gghh.name/dibtp/2015/11/10/function-calls-in-c-the-boring-specs.html) - 從遞迴學習 function call - 用 GDB 執行和測試 `infinite.c` - 瞭解 stack空間與C計算語法間的變化 - 依據 [Done Is Better Than Perfect](http://gghh.name/dibtp/2015/11/11/function-calls-in-c-practical-example.html) 實作時,發現記憶體並無 L1、L2 且 無法指定位址(如:`(gdb) break *0x40057d`) - stack-based buffer overflow - - 藏在 Heap 裡的細節 - 在測試實作 free()釋放記憶體時,沒有任何的 error, 如下 ```c #include <stdlib.h> int main() { int *p = (int *) malloc(1024); free(p); free(p); return 0; } ``` ```shell $ gcc -g -o free free.c -Wall $ ./free $ ``` - 為什麼 `glibc` 可以偵測出上述程式的 **``“double free or corruption”``** 呢? - 在搜尋中,找到上述 free.c 強制執行程式以觀察對同一塊記憶體重複 free() 所進行的偵測如下 ```shell $ MALLOC_CHECK_=1 ./free free(): invalid pointer Aborted (core dumped) ``` - `Set MALLOC_CHECK_ = 1` to disable this protection feature. This can achieved with the following commands, entered just before starting the application from that terminal: ```shell $ export MALLOC_CHECK_=1 $ /<path>/<application name> ``` - malloc / free - RAII 實作 - 我改寫了老師的程式碼才能 run,如下 ```clike #include <stdlib.h> #define autofree __attribute__((cleanup(free_stack))) __attribute__ ((always_inline)) inline void free_stack(void *ptr) { free(*(void **) ptr); } int main(void) { autofree int *i = malloc(sizeof (int)); *i = 1; return *i; } ``` --- ### 你所不知道的 C 語言: 遞迴呼叫篇 - 遞迴(recurse = A loop within a loop; = A pointer to a pointer; = A recursion within a recursion;)vs. 迴圈/迭代(iterate) - 遞迴程式沒有你想像中的慢 - 分析 clang/llvm 編譯的輸出 (參數 -S -O2),不難發現兩者轉譯出的 inner loop 的組合語言完全一樣 > 利用 `$ diff -up gcd_itr gcd_rec` 指令,沒有發現其差別,證明程式一樣!![name=曾令燊] - 遞迴程式設計 > 原來前人在面對數學及計算機(資料結構、演算法)問題時的思維及解法,可從老師抽絲剝繭可見一般...其中的道理是難深但透過老師專業引導下變成可理解的!! > 利用動畫來視覺化排序(sorting),真的較文字容易理解!![name=曾令燊] --- ### 你所不知道的 C 語言: 前置處理器應用篇 - 不要小看 preprocessor > 查了 unicode,分號 ; (UTF-8: 0x3B)、希臘問號 ; (UTF-8: 0xCD 0xBE [cdbe],Alt +037E) 真的無法用眼睛去識別...||| > 搜尋查訪還有一些 prank skill, may be suggest changing all ’n’ characters to the Greek Heta ‘$η$’ (H key) or ‘v’ with Ni ‘$ν$’ (N key). > 很高興 gcc 可以檢查此錯誤,如下 ```shell $ gcc -o prank prank.c prank.c:2:14: warning: treating Unicode character <U+037E> as identifier character rather than as ';' symbol [-Wunicode-homoglyph] int a = 5; ^ prank.c:2:14: error: invalid suffix ';' on integer constant prank.c:3:2: error: expected ';' at end of declaration } ^ ; prank.c:3:2: error: expected '}' prank.c:1:12: note: to match this '{' int main() { ^ 1 warning and 3 errors generated. ``` > [name=曾令燊] - 開發物件導向程式時,善用 preprocessor 可大幅簡化開發 > 可用 gcc -E -P 觀察輸出,但不知道老師示範的 每行程式有 ''\\'' backslash,是如何產生的?![name=曾令燊] - 應用: ADT > 對於 _Generic 泛型使用方式來簡化程式的品味,感覺方便!![name=曾令燊] > --- ### 你所不知道的 C 語言: goto 和流程控制 - 和想像中不同的 switch-case - 真的是想像不到,所以運用之妙存乎一心... - Duff’s Device - 有關此段的程式碼,我覺得是這樣才對,如下 ```clike void dsend(int count) { if (!count) { puts("false"); return; } int n = (count + 7) / 8; switch (count % 8) { case 0: do { puts("case 8"); case 7: puts("case 7"); case 6: puts("case 6"); case 5: puts("case 5"); case 4: puts("case 4"); case 3: puts("case 3"); case 2: puts("case 2"); case 1: puts("case 1"); } while (--n > 0); } } ``` - C程式可善用數學、巧思來coding... - co-routine 應用 - 有實作 user_thread 的程式,但對於透過 `互動式操作結束此程式` -- 不知是如何執行?! --- ### 你所不知道的 C 語言: linked list 和非連續記憶體操作 - 資料封裝 - 實作 [**`dlist`**](https://github.com/laserswald/dlist) > 瞭解利用 header file 來定義 linked list 各項功能 --- ### 你所不知道的 C 語言:技巧篇 - Implementing smart pointers for C - 到原作者的 [libcsptr](https://github.com/Snaipe/libcsptr) 探索,發現真的是需要好好的消化一番! :sweat_smile: --- ### GNU/Linux 開發工具: - 為什麼該接觸 GNU/Linux 開發工具(研究平台,專業視野,工作機會,實作導向) - 輕鬆學會 Windows 10 / Ubuntu 雙系統安裝 - 安裝 GNU/Linux -- Lubuntu 18.04 64-bit 版本 > 為什麼「不該」安裝 Linux 在虛擬機器呢?因為在課程對應的練習中,我們會透過 Linux 存取硬體暫存器和各式周邊...倘若透過額外的虛擬機器,不僅上述執行時間不精確,某些硬體暫存器和周邊 (如:PMU) 甚至無法存取。 > 所以說,目前VM虛擬機無法模擬100%硬體,那 Docker 呢?! > [name=曾令燊] - 從無到有學習HackMD - HackMD – LaTeX 語法與示範 > 實際繕打文件即可練習了,另外,喜歡它有支援 L^a^T~e~x排版及數學式(例:歐拉公式 $e^{i\pi}$ + 1 = 0)... > [name=曾令燊] - 熟悉 Git 和 GitHub 操作 (已建立帳號 [dvsseed](https://github.com/dvsseed)) > 因為 GitHub 被 微軟M$ 收購了,好像大家大多搬到 [GitLab](https://about.gitlab.com/) 了 > [name=曾令燊] - 編輯器:Visual Studio Code (已安裝) - 終端機和 Vim 設定 (已安裝 Plugin) - vim 命令圖解 (這個圖解畫的好容易看懂!!) - Makefile 語法和示範 (實際寫才會記得) - Linux效能分析工具: Perf (將於後續程式中應用之) - Linux製圖工具: gnuplot (實際操作發現其視覺功能真棒) --- # 2018q3 第 1 週測驗題 ### 測驗 `1` 考慮以下程式碼: ```C int my_xor(int x, int y) { return (x | y) OP1 (OP2 x OP3 OP4 y); } int main() { int x = 3, y = 5; return my_xor(x, y); } ``` `my_xor` 嘗試不用 XOR 運算子做出等效的 XOR 運算,其中 OP1, OP2, OP3, OP4 都是 operator,請補完實作。 ==作答區== 程式碼,如下 ```shell $ cat my_xor.c int my_xor(int x, int y) { return (x | y) & (~ x | ~ y); } int main() { int x = 3, y = 5; return my_xor(x, y); } $ gcc -o my_xor my_xor.c -Wall && ./my_xor $ echo $? 6 ``` 我只會用笨方法(先消去 % mod, & address of, * value of, 再手算 bitwise)...如下 $A \oplus B = (A+B)(\overline{A} + \overline{B}) = (A \vee B) \wedge (\neg{A} \vee \neg{B})$ Ref.[XOR--wikipedia](https://zh.wikipedia.org/wiki/%E9%80%BB%E8%BE%91%E5%BC%82%E6%88%96) ```shell (x | y) = 0111 (~x | ~y) = 1110 (x | y) & (~x | ~y) = 0110 0011 bitwise OR)0101 ---- 0111 1100 bitwise AND)1110 <= bit OR)1010 ---- ---- 0110 1110 ``` --- :::warning 初步完稿 :sweat: >[time=Mon, Oct 1, 2018 8:25 PM] ::: ###### tags: `sysprog2018` `C` `CLANG` `HackMD`