--- 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`。