# 2018Q3 Homework 1
contributed by <`0xff07`>
---
## Stringification
C99 新增。可以將一個表示式轉化成一個字串。如:
```C
#define PRINT_EXPR(EXP) do{printf("%s\n", #EXP);}while(0)
int main()
{
PRINT_EXPR(return 0);
}
```
將輸出:
```shell
$ return 0
```
### 應用
#### `assert`
使用 `assert` 時,若 `assert` 內部的表示式不成立,會將該表示式顯示出來。
## Concatenation
使用 `##` 來表示。會把兩個表示式連接起來。跟巨集搭配使用可以有程式碼生成(code generation)的效果。
### 應用
### 程式碼生成
可以搭配巨集,簡化程式碼。藉此達成類似物件導向的功能。如:
```C
#define DECLARE_OBJECT(name) \
struct __##name##_node; \
typedef struct __##name##_node *name##_node; \
struct __##name##_node { \
name element; \
name##_node next; \
}; \
void append_##name(const name *X, name##_node *list); \
void delete_##name##_list(name##_node *list);
DECLARE_OBJECT(light)
DECLARE_OBJECT(rectangular)
DECLARE_OBJECT(sphere)
```
### Q:
為什麼不使用 `void*` 指標,如
```C
struct generic_node {
void *data;
struct generic_node *next;
};
```
並且把函式宣告成:
```C
void generic_append(void *data, struct generic_node **list)
```
並且在使用時都手動轉型呢?這樣也不用每有新的資料型態就需要手動增加一次 `DECLARE_OBJECT(some_new_type)`。或是有什麼維護上的考量嗎?
:::info
原本是想降低 dispatch 成本,避免用 polymorphism 手段,畢竟可重用的物件不多
:notes: jserv
:::
## _Genereric
C11 出現關鍵字。可以依照不同資料型態呼叫相應的函數。
## GNU/Linux Process Structure
複習 APUE 跟恐龍書
## 函式呼叫篇
### 「不純」的函數
跟數學上的函式比起來,C 語言的「函數」其中一個不一樣之處,是「有能力產生副作用」。如 I/O 類的函數除了回傳一個值,也會對進行輸入輸出。甚至可說 C 語言幾乎都是仰賴副作用進行的(如:回傳後將值儲存在變數中)。
### 沒有 First Class Function
參照[維基百科](https://en.wikipedia.org/wiki/First-class_function)中關於 First-class Function 的說明。簡略的說明:函數並不能像整數, 浮點數一樣任意地修改並回傳。這點使得 C 語言難做純的函數式程式設計。
有找到搭配 gcc 可以做到 [curry 的例子](https://zenhack.net/2013/09/04/first-class-functions-in-c-because-thats-totally-a-good-idea.html)。但嚴格來說 curried 後的結果並不是一個函數,而是將函數與變數暫存起來,並且強制在 call stack 插入記起來的變數。
查資料時發現 C 可以實作 [Y-Combinator](https://rosettacode.org/wiki/Category:C)。但目前還沒看懂這個程式。
## 遞迴呼叫篇
數學上即有遞迴的概念。 $\lambda-\text{calculus}$ 中所有合法的表示法 $\Lambda$ 為:
$$
\begin{cases}
\frac {x \in V}{x \in \Lambda} \newline\newline
\frac {M \in \Lambda \qquad N \in \Lambda}{(MN) \in \Lambda} \newline\newline
\frac {M \in \Lambda \qquad x \in V}{(\lambda x.M) \in \Lambda}
\end{cases}
$$
好處為「數學即程式」,只要數學上的遞迴定義寫出來,相應的程式也完成。
在函數式程式語言可以進行 program calculation 「推導」出新的演算法。另外,只要有是迭構地定義出來的資料結構,均可以有遞迴的構造。如 Haskell 的 `foldr` 。
函數式程式設計的其中一個重要精神是函數呼叫沒副作用。
遞迴有效能較慢的迷思,但編譯器最佳化有機會使兩者的編譯結果類似或相同。
### Q
費波那契遞迴解法的複雜度應該要是:
$$
O\left(\left(\frac {\sqrt{5} + 1}{2}\right)^n\right)
$$
而不是直播講義中的 $O(2^n)$ ?
:::info
太好了,請協助修正共筆,感謝
:notes: jserv
:::
有沒有可能把常用的費式數列值建好表存檔,需要使用時再 `mmap` 搬進程式中,以達成均攤 $O(1)$ 的時間呢?
### 指標的定義
> C99 [6.2.5] **Types**
>
> A pointer type may be derived from a function type, an object type, or an incomplete type, called the referenced type. A pointer type describes an object whose value provides a reference to an entity of the referenced type. A pointer type derived from the referenced type T is sometimes called ‘‘pointer to T’’. The construction of a pointer type from a referenced type is called ‘‘pointer type derivation’’.
「指標」指的是一個值能夠作為以下三種資料型態的參照之資料型態:
1. Function Type:即 function pointer, 指向一段可執行程式之起始點。
2. Object Type:即「任何佔據明確大小記憶體」的東西。如整數、
3. Incomplete Type:如僅有宣告,卻沒有定義的 `struct`
而相應地,那些「被參照」的資料形態,泛稱「referenced types」。
其中第 3. 可引申出「Forward Declaration」的開發技巧,如 oltk :
```C
struct oltk; // 宣告 (incomplete type, void)
struct oltk_button;
typedef void oltk_button_cb_click(struct oltk_button *button, void *data);
typedef void oltk_button_cb_draw(struct oltk_button *button,
struct oltk_rectangle *rect, void *data);
struct oltk_button *oltk_button_add(struct oltk *oltk,
int x, int y,
int width, int height);
```
使用介面僅揭露於 `.h` 檔中,實作由 `.c` 決定。這樣做的另外一個好處是:若程式使用動態連結,則實作有修改時,僅需連結至新的實作,不用將整個程式重新編譯。如直播資料中的:
## Pointer vs. Array
下列 4 種的結果是相同的:
`x[4]`, `*(x + 4)`, `*(4 + x)`, `4[x]`
都是 `*(x + 4)`
若使用組合語言理解,不需感到意外。因為組合語言中 load 類的指令大多都是給定起始位址與 offset ,並將兩者相加計算出資料的位置。
## Linux 採用 LPD64
「指標有 8 個 byte」「整數有 4 個 byte」之類的講法,實際上是在指「[data model](https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models)」。事實上不同的作業系統對數值的位元數目可以有不同規範,因此以上這些說法並不嚴謹。這些規範常用 I (Integer)、L(Long)、LL(Long Long)、P(Pointer) 等等英文後面附加數字說明。如 LP64 指 `long` 、 `long long` 、指標均為 64 位元。