###### tags: `sysprog2018` # 2018q3 Homework3 (rewiew) contributed by < `goho302jo03` > --- ## Week 3 \#2 ```C= int main() { union { int a; char b; } c = { .a = 1 }; return c.b == 1; } ``` ### Little-Endian vs Big-Endian 不同的 CPU 儲存資料的方式會有差異,將低序字節儲存在起始位址的稱作 little-endian;將高序字節存在起始位址的稱作 big-endian。 ![](https://i.imgur.com/crGgpVt.png) ![](https://i.imgur.com/S8L4Sc6.png) ### Union 型別 Union 是一種將不同 data types 儲存在同一個記憶體空間的特殊自訂型別,一次只會儲存一個變數資料,且會以宣告的變數型態 size 最大的變數空間作為記憶體空間。 先測試 union 的大小 ```C= #include <stdio.h> int main() { union { char b; }c; printf("%d", sizeof(c)); } ``` ``` $ ./test.out 1 ``` ```C= int main() { union { int a; char b; }c; printf("%d\n", sizeof(c)); } ``` ``` $ ./test.out 4 ``` 接著測試是否共用同一段記憶體 ```C= int main() { union { int a; char b; } c; printf("%p\n", c.a); printf("%p\n", c.b); } ``` ``` $ ./test.out 0xa41df930 0x30 ``` 因為 union 內的變數共用同一塊記憶體,且 int 為 4 bytes,char 為 1 個 bytes,所以若 c.a 輸出 0xa41df930 則 c.b 輸出 0x30 :::info 若上面程式碼改成 ```C= int main() { union { int a; char b; } c; c.a = 0xffffffff; printf("%p\n", c.a); printf("%p\n", c.b); } ``` 為什麼輸出會是 ``` $ ./test.out 0xffffffff 0xffffffff ``` 而不是 ``` $ ./test.out 0xffffffff 0xff ``` ::: ### 題目運作原理 ```C= c.a = 1 ``` 如果是 big-endian ,則 c.b 會取到下面紅框內的值 ![](https://i.imgur.com/nQIPQ4D.png) 如果是 little-endian ,則 c.b 會取到下面紅框內的值 ![](https://i.imgur.com/efC0Sdl.png) ## Week 3 \#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; } ``` ### bit field 在 struct 結構型態中,可以定義各種不同長度的 bit 位元型態,以節省儲存空間 ```C= struct on_off { unsigned aa : 1; unsigned bb : 1; int cc; // 4 bytes unsigned dd : 4; // this and unsigned : 4; // this and unsigned ee : 1; // this bitfields are next to each other unsigned : 0; unsigned ff : 1; // this bitfield is at a 4 bytes boundary. } kitchen; ``` 呈現以下這種情況 ![](https://i.imgur.com/uP63EOf.png) 共需要 16 bytes 的記憶體空間 - Bit fields with a length of 0 must be unnamed. Unnamed bit fields cannot be referenced or initialized. A zero-width bit field can cause the next field to be aligned on the next container boundary where the container is the same size as the underlying type of the bit field. :::info ```C= int main() { struct on_off { unsigned aa : 1; unsigned bb : 1; int cc; // 4 bytes unsigned dd : 4; // this and unsigned : 4; // this and unsigned ee : 1; // this bitfields are next to each other unsigned : 0; // cause the next field to be aligned on the next container boundary */ char ff; char gg; } t; printf("%ld\n", (uintptr_t) &t.gg - (uintptr_t) &t.ff); } ``` 以上這段程式碼的輸出結果會是 ``` $ ./test.out 1 ``` 以我的理解,下面這段程式碼更改成了 `short gg;`,應該輸出結果會不變 ```C= int main() { struct on_off { unsigned aa : 1; unsigned bb : 1; int cc; // 4 bytes unsigned dd : 4; // this and unsigned : 4; // this and unsigned ee : 1; // this bitfields are next to each other unsigned : 0; // cause the next field to be aligned on the next container boundary */ char ff; short gg; } t; printf("%ld\n", (uintptr_t) &t.gg - (uintptr_t) &t.ff); } ``` 可是輸出結果卻是 ``` $ ./test.out 2 ``` 想請問一下記憶體配置會是像下面這樣嗎 還是說其實我哪裡弄錯了,謝謝 ![](https://i.imgur.com/Nxsfqv7.png) ::: ### C99/C11 6.5.3.2 Address and indirection operators #### Constraints The operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier. ## Week 2 \#4 考慮到以下程式碼: ```C= 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; } ``` 該如何修改,才能讓 func 得以變更到 main 裡頭 ptrA 的內含值? ### a pointer of a pointer 參考 [你所不知道的C語言:指標篇](https://hackmd.io/s/HyBPr9WGl#) 的範例 ```C= int B = 2; void func(int *p) { printf("Before: &p is %d\n", &p); printf("Before: p is %d\n", p); printf("Before: *p is %d\n", *p); p = &B; printf("After : &p is %d\n", &p); printf("After : p is %d\n", p); printf("After : *p is %d\n", *p); printf("After : &B is %d\n", &B); printf("After : B is %d\n", B); } int main() { int A = 1; int *ptrA; ptrA = &A; printf("Before: &A is %d\n", &A); printf("Before: A is %d\n", A); printf("Before: &ptrA is %d\n", &ptrA); printf("Before: ptrA is %d\n", ptrA); printf("Before: *ptrA is %d\n", *ptrA); printf("Before: &B is %d\n", &B); printf("Before: B is %d\n", B); func(ptrA); printf("After : &A is %d\n", &A); printf("After : A is %d\n", A); printf("After : &ptrA is %d\n", &ptrA); printf("After : ptrA is %d\n", ptrA); printf("After : *ptrA is %d\n", *ptrA); return 0; } ``` 會輸出以下 ``` $ ./test.out Before: &A is 1265417132 Before: A is 1 Before: &ptrA is 1265417136 Before: ptrA is 1265417132 Before: *ptrA is 1 Before: &B is 6295616 Before: B is 2 Before: &p is 1265417096 Before: p is 1265417132 Before: *p is 1 After : &p is 1265417096 After : p is 6295616 After : *p is 2 After : &B is 6295616 After : B is 2 After : &A is 1265417132 After : A is 1 After : &ptrA is 1265417136 After : ptrA is 1265417132 After : *ptrA is 1 ``` 從這個例子中可以知道 ptrA 並不會被改變 將程式碼改成以下 ```C= int B = 2; void func(int **p) { printf("Before: &p is %d\n", &p); printf("Before: p is %d\n", p); printf("Before: *p is %d\n", *p); printf("Before: **p is %d\n", **p); *p = &B; printf("-------------------------------------------\n"); printf("After : &p is %d\n", &p); printf("After : p is %d\n", p); printf("After : *p is %d\n", *p); printf("After : **p is %d\n", **p); printf("After : &B is %d\n", &B); printf("After : B is %d\n", B); } int main() { int A = 1; int *ptrA; ptrA = &A; printf("Before: &A is %d\n", &A); printf("Before: A is %d\n", A); printf("Before: &ptrA is %d\n", &ptrA); printf("Before: ptrA is %d\n", ptrA); printf("Before: *ptrA is %d\n", *ptrA); printf("Before: &B is %d\n", &B); printf("Before: B is %d\n", B); func(&ptrA); printf("After : &A is %d\n", &A); printf("After : A is %d\n", A); printf("After : &ptrA is %d\n", &ptrA); printf("After : ptrA is %d\n", ptrA); printf("After : *ptrA is %d\n", *ptrA); return 0; } ``` ``` $ ./test.out Before: &A is 116915084 Before: A is 1 Before: &ptrA is 116915088 Before: ptrA is 116915084 Before: *ptrA is 1 Before: &B is 6295624 Before: B is 2 Before: &p is 116915048 Before: p is 116915088 Before: *p is 116915084 Before: **p is 1 ------------------------------------------- After : &p is 116915048 After : p is 116915088 After : *p is 6295624 After : **p is 2 After : &B is 6295624 After : B is 2 After : &A is 116915084 After : A is 1 After : &ptrA is 116915088 After : ptrA is 6295624 After : *ptrA is 2 ``` 傳給 p 的值由 ptrA 變成 &ptrA,接著將 &B 賦值給 \*p,且因為 &ptrA = p ,所以就能夠改到 ptrA 的值。 ## Week 2 \#1 考慮到以下程式碼: ```C= const char *p; void dont_do_this(void) { const char c_str[] = "This will change"; p = c_str; } ``` 指出存在的問題和提出修正機制,需要用 C99/C11 規格解釋。 C99/C11 6.2.4 object referred out of lifetime is undefined behavior ### C99/C11 6.2.4 Storage durations of objects > 1. An object has a storage duration that determines its lifetime. There are four storage durations: static, thread, automatic, and allocated. Allocated storage is described in 7.22.3. > 2. The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address,33) and retains its last-stored value throughout its lifetime.34) If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime. ## Week 2 \#7 推敲以下程式碼的作用: ```C= void hex2(unsigned int x) { do { char c = "0123456789abcdef" [x & 0xf]; printf("char %c for %d\n", c, x); x >>= 4; printf("char %c for %d\n", c, x); } while (x); } ``` 以上這段程式碼是將 10 進位的數值轉換成 16 進位的數值, 將第 6 行刪掉較不會混淆 ```C= void hex2(unsigned int x) { do { char c = "0123456789abcdef" [x & 0xf]; printf("char %c for %d\n", c, x); x >>= 4; } while (x); } ``` ``` $ ./test.out Enter number: 63 char f for 63 char 3 for 3 # 63 -> 0x3f ``` ## Reference 1. [Big-Endian 與 Little-Endian 的差異與判斷程式碼](https://blog.gtwang.org/programming/difference-between-big-endian-and-little-endian-implementation-in-c/) 2. [C語言的struct結構型態之位元欄位(Bit+Fields)成員](https://blog.xuite.net/yhchiang/blog/151121693-C%E8%AA%9E%E8%A8%80%E7%9A%84struct%E7%B5%90%E6%A7%8B%E5%9E%8B%E6%85%8B%E4%B9%8B%E4%BD%8D%E5%85%83%EF%A4%9D%E4%BD%8D%28Bit+Fields%29%E6%88%90%E5%93%A1) 3. [What is zero-width bit field](https://stackoverflow.com/questions/13802728/what-is-zero-width-bit-field/13802888) 4. [ISO/IEC 9899:201x](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf) 5. [你所不知道的C語言:指標篇](https://hackmd.io/s/HyBPr9WGl#)