分享到plurk 分享到twitter 分享到facebook

版本 e402e5509807774ce09a80485a8fe510abb2804e

User/D4nnyLee

Changes from e402e5509807774ce09a80485a8fe510abb2804e to 7ab1de5565a07415b2fc9561578d8389e61c438f

# D4nnyLee(李韋杰)

* 成功大學資訊系 111 級

# Linux Kernel Internals 2021 春季班

## 問題選答

### a) 知道 x - y < 0 敘述為何不能寫為 x < y 嗎? (CS:APP 第 2 章)

因為這兩個敘述並不會在所有情況下都得到相同的結果。

假設 x, y 的型別都為 int8_t ,則 x, y 能表達的最小值就是 -128 ,因此很容易就能舉出 x = -128, y = 1 這個反例。

* x - y 得到的結果為 127,這是 signed integer underflow 所造成的結果,所以 x - y < 0 會得到 false 。
* x < y 就只是單純比較 x 和 y 的大小,而顯而易見的 -128 < 1 ,因此 x < y 會得到 true 。

由這個反例可以知道 x - y < 0 和 x < y 可能會得到不同的結果,因此不可以將兩個敘述劃上等號。

### d) 知道 Linux 核心 < include/linux/list.h> 裡頭 #define list_for_each_prev(pos, head) for (pos = (head)->prev; pos != (head); pos = pos->prev) 這樣的巨集到底在做什麼?以及 head 使用時需要加小括號,為何?

#### 巨集功能

list_for_each_prev(pos, head) 的作用為反向迭代 head 這個 linked list 上的元素。

pos 為用於迭代的指標,pos 和 head 的型別預期都是 struct list_head * 。

#### 為何需要小括號

為了避免因為 operator 優先順序的關係而造成非預期的行為。

在巨集裡面可以看到 pos != (head) 這段敘述,這邊就用 != 這個 operator 來舉例。

定義一個巨集 different(x, y) ,預期在 x 和 y 不相等的時候會回傳 true ,否則就回傳 false 。

```{.cpp}
#define different(x, y) (x != y)
```

如果只寫這樣的話如果呼叫 different() 時是寫 differernt(x, y = 3) ,巨集展開後就會變成 (x != y = 3)。

因為 != 會比 = 先執行,所以執行到 = 的時候左邊是 != 的結果,而這個結果並不是一個 [lvalue](https://en.cppreference.com/w/cpp/language/value_category)。
最後就會因為對非 [lvalue](https://en.cppreference.com/w/cpp/language/value_category) 賦值而造成編譯錯誤。

要修正這個錯誤就要將定義改成

```{.cpp}
#define different(x, y) ((x) != (y))
```

這樣展開之後就會變成 ((x) != (y = 3)),因為括號內的敘述會先被執行,所以 = 會先被執行,然後才是 != ,因此得到的結果就是預期的 != 的回傳值。

## 心得