# 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 ;
}
```
執行結果如下:

:::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;
};
```
那麼就只需要做隨尾填充