# 2018q3 Homework1 contributed by < [yichung279](https://github.com/yichung279) > ###### tags: `sysprog2018` ## 主題 * bitwise operation * 你所知道的 C 語言 (C基礎複習) * 你所不知道的 C 語言:開發工具與規格標準 * 你所不知道的 C 語言:指標篇 * 你所不知道的 C 語言:函式呼叫篇 ## bitwise operation(及測驗第一題延伸) * 簡介 : 逐個bit 作邏輯運算 * 應用 : bit & 0 : = 0 bit | 1 : = 1 bit ^ 1 : toggle 跟相反的布林作運算時,bit皆不會改變 配合0xff、0x80、shift、~ 可取特定位置的bit(s)或快速改bit * && / ||: 邏輯運算,數值只有0與~0 * 測驗延伸: 1.可配合卡諾圖,將真值表轉換為任何等價邏輯,並不只有xor可以轉換。 2.[nand/nor可以組合出所有邏輯運算子](https://market.cloud.edu.tw/content/vocation/control/tp_nh/logic/ch6/p3.htm) ## 你所知道的c語言(c基礎複習) ref: https://openhome.cc/Gossip/CGossip/ ### Ubuntu 16.04.5 LTS / GCC 5.5編譯器(我的環境)下的data type * 整數 short: 2 bytes int : 4 bytes long : 8 bytes * 浮點數 float : 4 bytes double : 8 bytes long double : 16 bytes * 字元 char : 1 byte > 這些規範不在規格書中,再 ieee754 及其他標準,這也保持了架構的靈活性。 ### 指標 * & : 取址 * * : 傳值 * 指標運算: 前進一個**資料型態的記憶體長度** * <s>雙重指標</s>: 就是**指標的指標** :::danger 沒有「雙重指標」這說法,請依據 C 語言規格書,只有 the pointer to the pointer :notes: jserv ::: ```clike #include <stdio.h> int main(void) { int var = 10; int *ptr = &var ; // ptr為var的指標,型態為int,占4bytes // var位址0x7ffedd04e2d2 // ptr == 0x7ffedd04e2d2 ptr + 1; // 0x7ffedd04e2d6 ptr + 2; // 0x7ffedd042da int **pptr = &ptr; // pptr為ptr的指標,型態為int,占4bytes *pptr; // *ptr == ptr == 10的指標 == 0x7ffedd04e2d2 **pptr; // **pptr == 10 return 0; } ``` :::info 善用 GDB,你可以直接傾印特定記憶體區間的內容,並用 macro 來客製化輸出 :notes: jserv ::: ## 你不所知道的 c 語言:開發工具和規格標準 ### C 語言發展: C是為了寫 Unix 而生的語言,而且可以自己編譯自己,先從從C 語言的子集合 C0 開始, C0 產生 C1 ,以此類推,一步步擴充規範。 C0 編譯器以組合語言開發。 見: [bootstraping](https://en.wikipedia.org/wiki/Bootstrapping_(compilers)) C0 有的關鍵字: ``` break goto void int double ``` ### ISO/IEC 9899: 直播中看到的疑問: * Q: what is lvalue/rvalue? * A: 這兩個名詞來自 `=` 兩邊的值,規格書中6.3.2.1: >The name ‘‘lvalue’’ comes originally from the assignment expression E1 = E2,..... --- * Q:為什麼會印出s的位址不是Hellow world 的ascii? ```clike char str[] = 'Hellow world'//string literal printf(%x, str) ``` * A: 根據規格書6.5.2.1 Array subscripting,`[]` 是pointer to object *type* 的 postfix,如 pointer to *int*,而str其實是 an array object ( a **pointer** to the initial element of an array object ) :::info 我不太理解這裡的 E1 到底是什麼,翻來翻去結果是 pointer to element (所以可以做指標運算)又是 array type (所以 E1 是lvalue 時,不能做modifiable value),導致 E1++ 不合法,但 E1+1合法。 所以 E1 是 pointer ,但是 array type 讓他行為不同嗎?而其他的pointer 會有 type嗎?(但[pjchiou的共筆](https://hackmd.io/J1wAu8KhTh20YzkalBmnUA?view)指 E1 不是指標,所以不能 E1++,有點困惑) ::: :::success array subscripting 限制了可用的 operator,請回頭看規格書 6.5.2 並且對照 gcc 編譯錯誤訊息 >6.5.2 第三點指出:`[]` 會把 E1 轉換成不能做lvalue的pointer[name=Yichung] 提及他人共筆時,應該述及 ID 或其他識別資訊。 :notes: jserv ::: --- * Q:我不會解析這段: >E1[E2] is identical to(*((E1)+(E2))) * A:由於 E1 是 pointer ,所以這裡是的指標運算。 &E1[12] = E1 + 12,指向一塊未初始化的記憶體。 --- * Q:以前計概的array在教/學什麼? * A:不知道 ### gcc 及gdb: compiler and debugger。 * gcc: gcc -o [執行檔名稱] [要編譯的檔案] * gdb: [ypChien的共筆](https://hackmd.io/c/SJGEwHytX/https%3A%2F%2Fhackmd.io%2Fs%2FBJKQ0hhu7) 有精美的指令整理。 ## 你不所知道的c語言:指標篇 * 頭腦體操: 查閱[運算子優先順序](https://zh.wikipedia.org/wiki/C%E5%92%8CC%2B%2B%E9%81%8B%E7%AE%97%E5%AD%90) 後,可以輕鬆作答。 * function pointer: ```clike int foo(); int main() { int (*funcptr)() = foo; } ``` ### `void *` 之謎: 指標操作有相關的風險,例如:用 "指向比較小 object 的 pointer" 指向 比較大的 object ,而導致不如預期的行為。 因為透過 `void*` 無法直接被存取的特性,我們便能保護 object。 危險: ![](https://i.imgur.com/vFv5ArW.png) 直接不給過: ![](https://i.imgur.com/vkzu2zN.png) 另有優點: [stack overflow 的討論](https://stackoverflow.com/questions/11626786/what-does-void-mean-and-how-to-use-it)中,提及'`void` is nothing, `void*` is everything',作為function parameter時,很有靈活性。 :::danger 文字訊息不要用圖片來展現,一來很難搜尋 (和複製貼上),二來視覺障礙的朋友無法觀看,自然無法跟你交流討論 :notes: jserv ::: ### pointer to pointer: 透過 address ,我們可以輕易地在 A function 修改 B function 的 object ,而如果這個object是pointer時,就需要pointer to pointer 了。 ### Type : * Type 決定了object或函式回傳值的expression * object types : describe objects * function types : describe functions * incomplete types : 描述物件但未決定物件(在記憶體中的)大小 * A pointer type 可以指向以下,並被稱為 referenced type * a function type, * an object type, * an incomplete type * 三位一體: Array, function, pointer types 都稱為 derived declarator types,都是從‘導’出來的型態 ### row major cache 待研讀: >Uno: 跟 cache line 有關 , column major 較容易會洗掉原本的 cache 內容 ref:[cenalulu的文章](http://cenalulu.github.io/linux/all-about-cpu-cache/) ### array vs pointer syntax sugar ```clike int *p, value; // 這樣宣告才不會搞混value的型態 int* p, value; // value實際上是int ``` 物件占了一定的空間,型態決定這些空間怎麼表示。 strcpy/strcat:使用到不該使用的記憶體,就會發生問題了。 cat:concatenate。 malloc:會失敗,失敗跟配置0都會回傳null >C語言把大家當成成熟的大人------宅色夫 argv有點可怕 :::warning :question: 延伸:python有argv嗎?有,sys.argv 為什麼還要有argparse?因為有人的程式很複雜 argv不夠用 但經實驗sys.argv 跟argparse的速度差了三個數量級 :::info 附上實驗方法和數據,理工人說話要精準 :notes: jserv >在我的筆電上,輸入一個環境參數,argv大約使用了0.000006s,argparser 約需要0.004s,一直想用程式做更大規模的實驗,有機會會補上完整數據。[name=yichung279] ::: ```clike void Foo(int i, float f); void Bar() { int num = 1; Foo(num, 2.0); } ``` num 與 2.0 即為 引數 (Argument),或稱為 函式引數 (Argument of a Function)。 i 與 f 則為 參數 (Parameter)。 function designator ,因為不是搭配 &, sizeof 使用,所以會被轉成為 pointer to function 就算 ***fptr,再多的 * 都鞥function designator。 ```clike #include <stdio.h> ``` ~~int *func1()~~ ```clike char *func1() { char *p = "hello world"; return p; } ``` ~~int *func2()~~ ```clike char *func2() { char p[] = "hello world"; return p; } int main() { printf("%s\r\n", func1()); printf("%s\r\n", func2()); } ``` `""` 會使用 static area 中的記憶體,進行 string literal,[] 會把上述的結果複製至stack中。 而func2() 指向的 array 在 func2 生命週期結束時,一起被釋放了,所以 func2() return值 便指向了 null。 但fun1() 指向 static area 裡的空間,func1生命週期結束後依然存在,所以 func1() return值 依然指向字串。 ::: info 詭異的事:一開始程式打錯 char 打成 int ,func1() 仍印出 "hello world"。 於是試圖做以下實驗: ```clike= int func() { long a = 0x12345678; return a; } int main(){} ``` gdb : whatis func():type = int x func(): 0x12345678 p func(): 305419896 難以理解一個 int type ,有 unsigned long 的行為。一個 object 不是以 type 決定 expression 嗎? function call 不能以 object 觀點檢視?(待我看完函式篇 >後來做到 big-endian / little-endian 的題目就懂了。-[name=yichung279] ::: ::: info OSX把 `/n` 拿掉會多印一位,GNU/linux不會多印,online compiler不會多印,為何有不同的行為,為何不是印到OS介入為止。(UB?) ::: ```clike= int main() { char s[] = "hello world"; s[11] = 'a'; puts(s); } ``` ## 你所不知道的C語言:函式呼叫篇 ### C 語言歷史: * 早期 C 語言 (1972-1973) -> K&R C (1976-1979) -> ANSI C (1983-1989) -> ISO * 「不允許 nested function」這件事簡化了 C 編譯器的設計 ### Something behind "Hello world" main printf 本身就是 function,涉及func call 呼叫標準函式庫,涉及linking before main memory layout