2018q3 Homework3 (review) ===== contribute by <`yichung279`> ###### tags: `sysprog2018` ## 作業要求 * 從 [第 1 週](https://hackmd.io/s/S1a9-YO_Q), [第 2 週](https://hackmd.io/s/BJA6LjbK7), [第 3 週](https://hackmd.io/s/BkyQskjK7) 選出你感興趣的 5 個題組進行作答,並且回答附帶的「延伸問題」 ## 第一周測驗題 測驗 `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,請補完實作。 **從真值表談起** 利用真值表與卡諾圖,配合一些邏輯運算技巧,可以更從容的面對邏輯運算。 * 真值表與卡諾圖 * 迪摩根定理、對合律、雙重否定 * xor 自反:不增加額外變數的交換 * nand:函數完備性 ### 真值表與卡諾圖 XOR 的真值表: |A|B|output| |:-:|:-:|:-:| |0|0|0| |0|1|1| |1|0|1| |1|1|0| 將A真與假做為第一二 row,將B真與假做為第一二 col,完成卡諾圖。 卡諾圖: |A\B|0|1| |:-:|:-:|:-:| |0|0|1| |1|1|0| 若我們想表達XOR,我們可以取 1(true) 的部分的集合,或者使邏輯運算的分配律,或者取 0(false) 的部分的反集合。 $\begin{split}XOR &= (\overline A \land B)\lor(A\land \overline B)&= \overline A B+ A\overline B \\ &= ......&=\overline A A + \overline {AB} + BA +B\overline B \\\ &=(\overline A \land \overline B)\lor(A\land B)&=\overline {AB} + AB\end{split}$ 事實上,題目到這裡就結束了,第二行的邏輯運算就是我們答案,但萬一今天需要進一步的轉換,我們就需要更強大的工具。 ### 迪摩根定理、雙重否定 舉個很實際的例子,在 NP-Prloem 的推演中,從 SAT 推導到 3-CNF 時,變牽涉到 [CNF](https://zh.wikipedia.org/wiki/%E6%9E%90%E5%8F%96%E8%8C%83%E5%BC%8F) 跟 [DNF](https://zh.wikipedia.org/wiki/%E5%90%88%E5%8F%96%E8%8C%83%E5%BC%8F)的轉換,我們要將 $(A\lor B)\land (C\lor D)$ 轉為 $(A\land B)\lor (C\land D)$。 迪摩根定理: $\neg(A\lor B) = \neg A\land \neg B$ $\neg(A\land B) = \neg A\lor \neg B$ 雙重否定: $\neg\neg A =A$ 並用,$(A\lor B)\land (C\lor D) \\ = \neg(\neg((A\lor B)\land (C\lor D))) \\ = \neg(\neg(A\lor B)\lor \neg(C\lor D)) \\ =\neg ((\neg A\land \neg B)\lor (\neg C\land \neg D))$ ### xor 自反:不增加額外變數的交換 自反: $A \oplus 0 = A$ $A \oplus A = 0$ $B \oplus A \oplus A=B$ ```clike int swap(int x, int y){ x = x ^ y y = x ^ y x = x ^ y } ``` 以數學表示最後的 x,y $y = x \oplus y \oplus y$ $x = x\oplus y\oplus x\oplus y\oplus y$ ### nand 與函數完備性 提到 nand 是由於 nand 有兩大特色。 1.實作時,nand 所需的電晶體少於其他邏輯閘。 2.函數完備性,即 nand 可以變現出所有邏輯閘。 :::info :question: 程式中,運用 nand 計算會因此比較快嗎?有的話,有快到值得去做對應的優化嗎? ::: ## 第二周測驗題 測驗 `2` [指標篇](https://hackmd.io/s/HyBPr9WGl) 提到 signal 系統呼叫的原型宣告: ```C void (*signal(int sig, void (*handler)(int))) (int); ``` 該如何解析呢? * `*a[]` : array of pointers * `(*a)[]`: a pointer to an array * `*f()` : function return a pointer * `(*f)()`: a pointer to functon handler: a pointer to a function, taking a int as parameter, returning void. sig: int signal:a fuinction return a pointer to a function which takes a int and retruns void. 提示: 參閱 manpage: [signal(2)](http://man7.org/linux/man-pages/man2/signal.2.html) :::success 延伸問題: 解釋 signal(2) 的作用,並在 GitHub 找出應用案例 ::: ## 第二周測驗題 測驗 `4` 考慮到以下程式碼: ```clike int B = 2; void func(int *p) { p = &B; } int main() { int A = 1, C = 3; int *ptrA = &A; func(ptrA); printf("%d\n", *ptrA); return 0; } ``` ![](https://i.imgur.com/30cSCdC.jpg) 該如何修改,才能讓 `func` 得以變更到 `main` 裡頭 `ptrA` 的內含值? `func` 中,p 原本是個 pointer,隨後被 assign B 的 reference,一傳入 A 的 reference,便被改掉。 若要修改 prtA 的值,應傳入 pointer to ptrA,再以 `*` dereference,取出 value of pointer to ptrA,並 assign B的 reference 給 value of pointer to ptrA。 結論: 要更改 `main` 的物件時,都必須是透過`*`dereference 出原物件,而今天的物件是pointerpointer罷了。 加入一個 pointer to ptrA 便能解決這樣的問題。 ```clike int B = 2; void func(int **p) { *p = &B; } int main() { int A = 1, C = 3; int *ptrA = &A; /**********************/ int **ptr2ptrA = &ptrA; /*********************/ func(ptr2ptrA); printf("%d\n", *ptrA); return 0; } ``` :::success 延伸問題: 在 GitHub 找出使用 the pointer to the pointer 的 C 語言程式碼,並加以討論 ::: ## 第二周測驗題 測驗 `5` 以下程式是合法 C99 程式嗎? ```C #include <stdio.h> int main() { return (********puts)("Hello"); } ``` 請搭配 C 語言規格書解釋 根據 6.3.2.1-4 >A function designator is an expression that has function type. Except when it is the operand of the sizeof operator or the unary & operator, a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’. 除了 `sizeof()` 以及 `&` 外的運算子運算, function designator 會被轉成 pointer to function。所以不論多少`*`, 最後 puts (function designator) 都會被轉成 pointer to function。 繼續思考以下是否合法: ```C #include <stdio.h> int main() { return (**&puts)("Hello"); } ``` 繼續變形: ```C #include <stdio.h> int main() { return (&**&puts)("Hello"); } ``` 也會合法嗎?為什麼?請翻閱 C 語言規格書解說。 以 `*&` 搜尋 [c99](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf),相關註解: >Thus, &*E is equivalent to E (even if E is a null pointer), and &(E1[E2]) to ((E1)+(E2)). It is always true that if E is a function designator or an lvalue that is a valid operand of the unary & operator, *&E is a function designator or an lvalue equal to E. 所以 `*&put` 是 `puts`, `&*puts` 也是 `puts`,程式仍然可以合法運作。 而事實上,6.5.3.2-3 >The unary & operator yields the address of its operand. If the operand has type ‘‘type’’,the result has type ‘‘pointer to type’’. function designator 使用 `&` 運算本來就會得到 pointer to function,跟 *function designator 的結果一樣。(Types 可分為 object type, function type, incomplete type。--根據 6.2.5-1 ) :::info :question: 明明 `&` 也會得到 pointer to function returning type,為何 6.3.2.1-4 ,要那樣描述? ::: ## 第三周測驗題 測驗 `2` 考慮以下程式碼,在 little-endian 硬體上,會返回 `1`,反之,在 big-endian 硬體上會返回 `0`: ```C int main() { union { int a; char b; } c = { .a = K1 }; return c.b == K2; } ``` 補完上述程式碼。 K1 = 1, K2 = 1 * little-endian: 最低位位元組,儲存在最低記憶體位址上。 big-endian: 最低位為元組,儲存在最高記憶體位址上。 * 由於 union 語法,整個 c,包含 c.a、c.b 共用同一個記憶體位址,存取時,不論little-endpian,或是 big-endian,都會從最低記憶體位址存取。 而我們知道 int 為 4 bytes,char 為 1 byte,1 的二補數為 `00000001`, 因此我們得知若在 little-endian硬體上,當我們以 int 寫入 1 時, 記憶體如下圖表示,而我們以 c.b 拿 char 大小的記憶體,只會拿到 a 裡的 `01`。 |記憶體位置|記憶體內容| |:---:|:-:| | a+3 | 00 | | a+2 | 00 | | a+1 | 00 | | a | 01 | :::success 延伸問題: 解釋運作原理,並找出類似的程式碼 :::