---
tags: linux2022
---
# 2022q1 Homework (quiz3)
contributed by < `hankluo6` >
> [第 3 週測驗題](https://hackmd.io/@sysprog/linux2022-quiz3)
## 測驗 `1`
### 運作原理
```c
#define GENMASK(h, l) \
(((~0UL) >> (LEFT)) & ((~0UL) >> (l) << (RIGHT)))
```
`(~0UL) >> (LEFT)` 將所有 bit 向右移,所以其 MSB 數來 bit 為 0 的數目與 `LEFT` 相等。可推出 `(~0UL) >> (l) << (RIGHT)` 需將從 LSB 數來的 bit 設為 0。
可得 `LEFT = 63 - h`,`(~0UL) >> (l) << (RIGHT)` 需把 LSB 部分的 bit 清空,故 `RIGHT = l`。
---
## 測驗 `3`
### 運作原理
```c=
#include <stdint.h>
uint8_t rev8(uint8_t x)
{
x = (x >> 4) | (x << 4);
x = ((x & 0xCC) >> 2) | (EXP2);
x = ((x & 0xAA) >> 1) | (EXP3);
return x;
}
```
`(x >> 4) | (x << 4)` 將前四個位元與後四個位元交換,`((x & 0xCC) >> 2) | (EXP2)` 應要將每兩個位元互換,`0xCC` 表示將 3, 4, 7, 8 個位元又移,則須將 `0xFF ^ 0xCC = 0x33` 的位元左移,得 `EXP2 = (x & 0x33) << 2`,`((x & 0xCC) >> 2) | (EXP2)` 將每一個位元互換,`0xFF ^ 0xAA = 0x55`,故 `EXP2 = (x & 0x55) << 1`。
---
## 測驗 `3`
### 運作原理
```c
#define foreach_int(i, ...) \
for (unsigned _foreach_i = (((i) = ((int[]){__VA_ARGS__})[0]), 0); \
_foreach_i < sizeof((int[]){__VA_ARGS__}) / sizeof(int); \
(i) = ((int[]){__VA_ARGS__, 0})[EXP4])
```
利用 [Variadic Macros](https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html) 實現 `foreach`,其中 `_foreach_i` 用來記錄迴圈的次數,`i` 為目前的可變參數。
`unsigned _foreach_i = (((i) = ((int[]){__VA_ARGS__})[0]), 0` 剛進入迴圈時,透過 `(i) = ((int[]){__VA_ARGS__})[0]` 將 `i` 設定為可變參數的第一個 element,並使用 `,` operator 將 `_foreach_i` 設定為 0。
迴圈的條件式 `_foreach_i < sizeof((int[]){__VA_ARGS__}) / sizeof(int)` 為 `_foreach_i` 小於可變參數大小。
`(i) = ((int[]){__VA_ARGS__, 0})[EXP4]` 則需要得到下個可變參數,故 `EXP4 = ++_foreach_i`。另外 `_foreach_i` 會在可變參數大小後額外加一,才會跳出迴圈,故 `(int[]){__VA_ARGS__, 0}` 多一個 0 取不到陣列的值。
```c
#define foreach_ptr(i, ...) \
for (unsigned _foreach_i = \
(((i) = (void *) ((typeof(i)[]){__VA_ARGS__})[0]), 0); \
(i); (i) = (void *) ((typeof(i)[]){__VA_ARGS__, \
NULL})[EXP5], \
_foreach_no_nullval(_foreach_i, i, \
((const void *[]){__VA_ARGS__})))
```
與 `foreach_ptr` 相同,只是迴圈終止條件改為判斷 `i` 是否為 `null`,而 `i` 會在跑完可變變數時被設為 `null`,故 `EXP5 = ++_foreach_i`。