# 2018q3 Homework1 contributed by < `eating861227` > :::danger 你的提問呢?回顧你過去開發的程式,難道沒想到概念上有衝突或者激發出更多想法嗎? :notes: jserv ::: ## 本週學習目標 - [ ] [為什麼要深入學習](https://hackmd.io/s/HJpiYaZfl) - [ ] [你所不知道的 C 語言: 指標篇](https://hackmd.io/s/SJ6hRj-zg) - [ ] [你所不知道的 C 語言: 函式呼叫篇](https://hackmd.io/s/SJ6hRj-zg) - [ ] [你所不知道的 C 語言: 遞迴呼叫篇](https://hackmd.io/s/rJ8BOjGGl) - [ ] [你所不知道的 C 語言: 前置處理器應用篇](https://hackmd.io/s/S1maxCXMl) - [ ] [你所不知道的 C 語言: goto 和流程控制](https://hackmd.io/s/B1e2AUZeM) - [ ] [你所不知道的 C 語言: linked list 和非連續記憶體操作](https://hackmd.io/s/SkE33UTHf) - [ ] [你所不知道的 C 語言: 技巧篇](https://hackmd.io/s/HyIdoLnjl) - [ ] [GNU/Linux 開發工具](https://hackmd.io/c/rJKbX1pFZ) ## 指標篇 ### 函數指標 **宣告方式** **`回值型態 (*指標名稱)(傳遞參數);`** * 函式名稱本身就是指向該空間位址的參考名稱,當呼叫函式名稱時,程式就會去執行該函式名稱所指向的記憶體空間中之指令。 於是練習了一下: ```clike #include <stdio.h> void add() { printf("hello\n"); } int main() { int (*op)() = 0; printf("op:%d\n", op); op = add; printf("&add:%d\n", &add); printf("add:%d\n", add); printf("op:%d\n", op); printf("&op:%d\n", &op); op(); add(); return 0 ; } ``` 執行結果如下: ![](https://i.imgur.com/6FZjHkJ.jpg) :::danger 避免用圖片表示文字訊息,請善用 HackMD 支援的語法來顯示文字輸出 :notes: jserv ::: >嘗試過後發現函數指標和其他類型的指標的概念是相同的,不過對於"add",我就不懂了,為什麼"add"和"&add"是一樣的? :::info 先找出 C99/C11 規格書裡頭對 function pointer 相關的描述,詳閱後再來提問 :notes: jserv ::: --- * 指標不能有完整的算數,所以只能加減,不能乘除 * C 語言型態很重要! ```clike int A; int* ptrA = &A; int* ptrB = ptrA ptrA++; //合法 ptrA = ptrA+1; //合法 ptrB = ptrA*1; //GG ptrB = ptrA + ((int)ptrA*1) //強制轉型 ``` * Incomplete Type 只能宣告不能當物件,所以可以宣告一個pointer type ```clike struct object x; // GG struct object *x // OK ``` * 幫宣告的程式或解釋:`$ sudo apt-get install cdecl` ### void* 探討 * 任何函式若沒有特別標注返回型態,一律變成 int (伴隨著 0 作為返回值),但這導致無從驗證 function prototype 和實際使用的狀況 * void* 指標本身記憶體大小是確定的,但指向的東西是不確定 ```clike int main() { int* x = 0 ; void* y = 0 ; int a = *x ; int b = *y ; //不合法 要強制轉型 int c = *(int*)y;//y才有明確空間,dereference後也有明確空間 int e = *(double*)y;//牽涉到alignment } ``` ### Alignment (對齊) :::info 在x86或者ARM處理器上,C語言存資料並非隨便從任一位置開始存,是需要對齊的 ! ( *self-aliged* ) 這樣的目的是希望可以快速查訪。所以如果存取空間是 2 bytes ,則需起始於可以被 2 整除的地址,其他以此類推。 ::: **基本類型的儲存空間** >1 byte : char , unsigned char , signed char >2 bytes : (unsigned) short >4 bytes : (unsigned) int , (unsigned) long , float , 32位元的指標 >8 bytes : double , long double , 64位元的指標 ==所以宣告順序是個學問囉~== >如果我宣告以下這些變數 ```clike char *p; char c; int x; ``` >實際上內存的分布為: ```clike char *p; /* 4 or 8 bytes */ char c; /* 1 byte */ char pad[3]; /* 3 bytes */ 這是個無用的空間,又稱為slop int x; /* 4 bytes */ ``` >在64位元中,如果改成這樣,就不會有 slop 的情形了 ```clike char *p; /* 8 bytes */ long x; /* 8 bytes */ char c; /* 1 byte ``` #### Struct 的對齊 :::info Struct 的物件會依照最寬的成員對齊,編譯器這樣做是為了保證所有成員是 self-aligned ::: 例如: ```clike struct foo1 { char c; struct foo7 *p; short x; }; ``` 實際上: ```clike struct foo1 { char c; /* 1 byte */ char pad1[7]; /* 7 bytes */ struct foo7 *p; /* 8 bytes */ short x; /* 2 bytes */ char pad2[6]; /* 6 bytes */ }; ``` 要怎麼解決 ? 按大小重排: ```clike struct foo2 { struct foo2 *p; short x; char c; }; ``` 那麼就只需要做隨尾填充