2018q3 Homework3 (review) === contributed by < [brian208579](https://github.com/brian208579) > ## Week 2 - test 2 [指標篇](https://hackmd.io/s/HyBPr9WGl) 提到 signal 系統呼叫的原型宣告: ```C void (*signal(int sig, void (*handler)(int))) (int); ``` 該如何解析呢? 提示: 參閱 manpage: [signal(2)](http://man7.org/linux/man-pages/man2/signal.2.html) :::success 延伸問題: 解釋 signal(2) 的作用,並在 GitHub 找出應用案例 ::: - 考慮以上程式碼 : 上述程式碼是一個函數 signal() 的聲明,而 signal 這個函數擁有兩個參數,第一個為整數型態的 sig ,第二個為指標函數 handler,而這個 handler 指向一個整數,並且沒有回傳值。 - 這個函式在作業 2 裡的 qtest.c 裡就有 ```clike= static void queue_init() { fail_count = 0; q = NULL; signal(SIGSEGV, sigsegvhandler); signal(SIGALRM, sigalrmhandler); } ``` 程式在執行的階段若接收到 `SIGSEGV` 訊號就執行 `segsegvhandler` 函式,接收到`SIGALRM` 訊號就執行 `signalrmhandler` 函式。 --- ## Week 3 - test 2 考慮以下程式碼,在 little-endian 硬體上,會返回 `1`,反之,在 big-endian 硬體上會返回 `0`: ```C int main() { union { int a; char b; } c = { .a = K1 }; return c.b == K2; } ``` 補完上述程式碼。 ==作答區== K1 = ? * `(a)` 0 * `(b)` 1 **( answer )** * `(c)` -1 * `(d)` 254 K2 = ? * `(a)` 0 * `(b)` 1 **( answer )** * `(c)` 254 :::success 延伸問題: 解釋運作原理,並找出類似的程式碼 ::: - 考慮以上程式碼 : **Union**是一種將不同data types儲存在同一個記憶體空間的特殊自訂型別,**union**一次只會儲存一個變數資料。換句話說,所有在union宣告的變數會共享同一個記憶體空間,而且會已宣告的變數型態size最大的變數空間作為記憶體空間。 如程式碼所見, **int** 大小為 4 個 bytes ,**char** 為 1 個 bytes ,故 union 所宣告的記憶體空間為 4 個 bytes,也就是 **c** 的記憶體空間為 4 個 bytes 。 在 big-endian 硬體上,資料的最高位元組在最低記憶體位址,資料的最低位元組在最高記憶體位址,依次排列,反之在 little-endian 硬體上,資料的最高位元組在最高記憶體位址,資料的最低位元組在最低記憶體位址。 **示意圖 ( 記憶體位址由低 ➞ 高 ) :** `假設有一 32 位元(bits)的整數資料為 0x12345678` ```graphviz digraph { node[shape=record] littleptr[label="little-endian", shape=plaintext] little [label="0x78|0x56|0x34|0x12"] } ``` ```graphviz digraph { node[shape=record] bigptr[label="big-endian", shape=plaintext] big [label="0x12|0x34|0x56|0x78"] } ``` - 故如以上原程式碼如是操作在 little-endian 硬體上的話,會 return 1 ,操作在 big-endian 硬體上的話,會 return 0。 **示意圖 ( 記憶體位址由低 ➞ 高 ) :** ```graphviz digraph { node[shape=record] littleptr[label="little-endian", shape=plaintext] little [label="0x01|0x00|0x00|0x00"] } ``` ```graphviz digraph { node[shape=record] bigptr[label="big-endian", shape=plaintext] big [label="0x00|0x00|0x00|0x01"] } ``` --- ## Week 3 - test 1 考慮以下程式碼 : ```C= #include <stdio.h> #include <stdint.h> struct test { unsigned int x : 5; unsigned int y : 5; unsigned int z; }; int main() { struct test t; printf("Offset of z in struct test is %ld\n", (uintptr_t) &t.z - (uintptr_t) &t); return 0; } ``` 在 GNU/Linux x86_64 環境中執行,得到以下輸出: ``` Offset of z in struct test is 4 ``` 倘若將第 10 和第 11 換為以下: ```C=10 printf("Address of t.x is %p", &t.x); ``` 會發生什麼事? ==作答區== * `(a)` 印出類似 `0x7ffd144e8ad4` 的輸出,也就是 `t` 結構物件的位址 * `(b)` 印出類似 `0x7ffd144e8ad4` 的輸出,和 `t` 結構物件差距 4 bytes * `(c)` 可以印出數值,但這與編譯器實作有關,不能解讀 * `(d)` 編譯失敗,不能這樣宣告結構體的成員 ( **answer** ) * `(e)` 編譯失敗,不能將指標指向沒有對齊 1 byte 的結構體成員 參考資料: [Portable Bit Fields in packetC](https://link.springer.com/content/pdf/10.1007/978-1-4302-4159-1_34.pdf) :::success 延伸問題: 解釋原因,並且找出 C99/C11 規格書對應的描述 ::: - 考慮以上程式碼 : 由 **C99** 規格書的 **6.7.2.1 Structure and union specifiers** 中的第 11 點中提到 The unary & (address-of) operator cannot be applied to a bit-field object; thus, there are no pointers to or arrays of bit-field objects. **由上敘述可知道 & (address-of) operator 不能使用在 bit-filed 上** --- ## Week 2 - test 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` 的內含值? :::success 延伸問題: 在 GitHub 找出使用 the pointer to the pointer 的 C 語言程式碼,並加以討論 ::: - 考慮以上程式碼 : 如要讓 `func` 得以變更到 `main` 裡頭 `ptrA` 的內含值,只要修改原程式碼的第2行更改為指標的指標即可,如下所示 ```C=02 void func(int **p) { *p = &B; } ``` ```C=06 func(&ptrA); ``` --- ## Week 2 - test 5 以下程式是合法 C99 程式嗎? ```C #include <stdio.h> int main() { return (********puts)("Hello"); } ``` 請搭配 C 語言規格書解釋 繼續思考以下是否合法: ```C #include <stdio.h> int main() { return (**&puts)("Hello"); } ``` 繼續變形: ```C #include <stdio.h> int main() { return (&**&puts)("Hello"); } ``` 也會合法嗎?為什麼?請翻閱 C 語言規格書解說。 - 考慮以上程式碼 : 根據 **C99** 規格書中的 **6.5.3.2** 第四點 The unary * operator denotes indirection. If the operand points to a function, the result is a function designator 又根據 **C99** 規格書中的 **6.3.2.1** a function designator with type ''function returning type'' is converted to an expression that has type ''pointer to function returning type''. 所以 pointer to function 變成 function designator ,然後 function designator 又被轉成 pointer to function ```Cilck= #include <stdio.h> int main() { return (**&puts)("Hello"); ``` - 考慮以上程式碼 : 根據 **C99** 規格中的 **6.5.3.3** 註解 **87** 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. 所以 *&puts 還是 function designator ,在 dereference 一樣還是 function designator ```click= #include <stdio.h> int main() { return (&**&puts)("Hello"); } ``` - 考慮以上程式碼 : 根據 **C99** 規格書中的 **6.5.3.3** 註解 **87** &*E is equivalent to E (even if E is a null pointer) 由以上可知還是合法的。