contributed by < Wallmountain >
在 Linux 核心原始程式碼,include/linux/bitfield.h 提及一個巨集 GENMASK
,其作用是依據給定的範圍,產生連續的 bitmask,例如:
GENMASK(6, 4)
產生 GENMASK(39, 21)
產生 0x000000ffffe00000 (64 位元)已知我們使用的微處理器架構為 64 位元,且 unsigned long
為 8 位元組寬度 (符合 LP64 資料模型),以下是可能的 GENMASK
巨集的定義:
可將 GENMASK
簡單分成兩個流程:
h
個位元開始到最高位元都為 0 => (~0UL) >> (63 - h)
l - 1
個位元開始到最低位元都為 0 => (~0UL) >> (l) << (l)
並將以上兩條件取 &
就為 GENMASK
產生方式
比較 Linux 核心 GENMASK 巨集的實作,闡述其額外的考量
我們實作的程式碼的第二個條件使用了兩次位移,而 linux的實作則避免了兩次位移的操作
l - 1
個位元開始到最低位元都為 0 => (~0UL) << (l)
舉出 Linux 核心原始程式碼中二處
GENMASK
巨集
GENAMASK
產生從最低位元到第mult->width - 1
為1而剩下為0的值PLL 為 Phase-locked loop 的縮寫,是一種控制系統,最簡單的是由一個可變頻率振盪器和一個反饋迴路中的相位檢測器組成的電子電路,一般而言,模擬 pll 會包含四個部分,分別為相位頻率偵測器 (Phase Frequency Detector), 低通濾波器, 壓控振盪器, 反饋迴路(Feedback)。
接著,這段程式碼中使用到 GENMASK
,列出的程式碼擔任分頻器的角色
在裡面的註解表示最多有 7 個位元為整數的部分,而剩餘的部分則為小數的部分
GENMASK
,用以確保 pcw
的範圍c
變數設為1c
為 1,則加 1,代表將剛剛小數的部分進位在程式碼的部分,主要是將小數的部分去除,若有小樹則將其無條件進位到個位數,並且在最後回傳分頻後的結果
函式 fo_fd 接受指定的地址 v,填入一個 fd 結構體並確保第一個成員的地址得以依據〈你所不知道的 C 語言:記憶體管理、對齊及硬體特性〉描述,對 4 個位元組進行向下對齊(即 C++ Boost 描述的 align_down)。其中 struct foo; 運用〈你所不知道的 C 語言:指標篇〉提及的 forward declaration 技巧,可在實作階段才提供具體 struct foo 的定義。
~3
最後兩個位元為0的特點進行對齊,並以 (struct foo *)
的 type 將地址指向對齊過後的位址考慮以下程式碼,能將輸入的 8 位元無號整數逐一位元反轉,如同 LeetCode 190. Reverse Bits 的描述
32 位元無號整數版本
延伸〈你所不知道的 C 語言:前置處理器應用篇〉,我們嘗試將 foreach 巨集 予以擴充:
使得支援以下寫法:
預期輸出如下:
__VA_ARGS__
為一個 variadic macro,支援不同數量的參數,利用 "..."
來表示一個或多個參數, 而 regular macro arguments 都須放在 "..."
前面
__VA_OPT__
去除了這個限制"##"
去除 regular macro arguments 和 variadic macro 之間的逗號,解決前置處理器延展之後的錯誤以上 foreach
的用途為利用 __VA_ARGS__
將後面傳入的參數變成 array,並用 for 迴圈存取裡面的內容
foreach_int
的 type 為 array of unsigned int,故要在 array 的最後加上0,判斷 array 的結尾
foreach_ptr
的 type 為 array of char *,故要在 array 的最後加上 NULL
,判斷 array 的結尾
針對 LeetCode 29. Divide Two Integers,以下是可能的實作:
一開始,將兩個傳入的參數賦值給型態為 unsigned int
的變數,是因為最小的負值轉成正的會溢出
利用二進位的特點進行除法,確認除數要向左位移幾次能到不超過被除數的最大數,此時被除數減去位移過後的除數,並記錄商數,持續這個步驟,直到被除數不大於除數
改善 code
初始程式碼用 while 迴圈來尋找除數要位移幾次才能比被除數大,而過程中進行了多次比較,而將其改善程利用 __builtin_clz
來找到兩個相差幾次位移,但還是需要一次比較來確定在進行這次位移之後,除數是否會大於被除數
改善方向可以盡力去尋找把 branch 去掉的可能性
針對 LeetCode 149. Max Points on a Line,以下是可能的實作:
考慮 ilog32 函式可針對給定的 32 位元無號數,計算扣除開頭的 0,最少需要多少位元才可儲存 (the minimum number of bits required to store an unsigned 32-bit value without any leading zero bits),以下是一個可能的實作:
以這個函式可以涵蓋的範圍為32個位元,每次去搜索當前剩餘範圍的一半,來加速一個一個搜索的速度
考慮以下 C++ 二元搜尋樹的程式:
函式 remove_data 嘗試將指定的資料,自給定的二元搜尋樹移除,但原本的 remove_data 過於冗長,於是我們可善用 indirect pointer 改寫為以下:
利用 indirect pointer 可以減少去判斷當前 node 狀態的分支。
考慮以下進行記憶體地址對齊 (alignment) 的巨集:
考慮以下巨集可得到二個表示式進行整數除法操作時,最接近的整數值:
參考執行結果:
- DIVIDE_ROUND_CLOSEST(7, 3) = 2
- DIVIDE_ROUND_CLOSEST(6, 3) = 2
- DIVIDE_ROUND_CLOSEST(5, 3) = 2
以題目而言,就是進行四捨五入,首先判斷被除數與除數是否同號
考慮以下計算整數開平方根的實作: