contributed by < ganoliz >
在 Linux 核心原始程式碼,include/linux/bitfield.h 提及一個巨集 GENMASK,其作用是依據給定的範圍,產生連續的 bitmask,例如:
GENMASK(6, 4)
產生 GENMASK(39, 21)
產生 0x000000ffffe00000 (64 位元)請問,
LEFT
= ?
RIGHT
= ?
我們可以知道 ~0UL 的值是 0 ~ 64 bits 的值都為 1 ,然後我們可以將這個 GENMASK 巨集拆成兩個部分:
(~0UL) >> (LEFT)
與 (~0UL) >> (l) << (RIGHT)
兩個部分。在假設為"非邏輯位移"的情況下(而且這裡是 unsigned long),那麼答案為 LEFT = 63 - h RIGHT = l
延伸問題:
考慮以下程式碼:
函式 fo_fd 接受指定的地址 v,填入一個 fd 結構體,並確保第一個成員的地址得以依據〈你所不知道的 C 語言:記憶體管理、對齊及硬體特性〉描述,對 4 個位元組進行向下對齊 (即 C++ Boost 描述的 align_down)。其中 struct foo; 運用〈你所不知道的 C 語言:指標篇〉提及的 forward declaration 技巧,可在實作階段才提供具體 struct foo
的定義。
請問,
EXP1
= ?
參考<jim12312321> 對於記憶體對齊的描述:
向上對齊(對 4 bytes 或 8 bytes 對齊):
未 alignment 前起始位置 :
alignment 後 :
向下對齊:
未 alignment 前起始位置 :
alignment 後 :
因此在對齊,我們可以知道(注意向下對齊是往記憶體位置的下面對齊)
延伸問題:
考慮以下程式碼,能將輸入的 8 位元無號整數逐一位元反轉,如同 LeetCode 190. Reverse Bits 的描述。
請問,
EXP2
= ?
EXP3
= ?
這裡就是老師 C語言 bitwise 講座提到的內容,基本上這段程式碼就是透過shift的方式一對一對的把高低位元交換。首先最高四個位元與最低四個位元交換。接著是 4 bits 的高兩個位元與低兩個位元交換,
因此
延伸〈你所不知道的 C 語言:前置處理器應用篇〉,我們嘗試將 foreach 巨集 予以擴充,使得支援以下寫法:
預期輸出如下:
對應的巨集定義:
請問,
EXP4
= ?
EXP5
= ?
根據巨集,我們可以進行拆解,在 foreach_int 迴圈裡, ( i ) = ((int []){VA_ARGS})[0] ,就是指目前在 後面參數中的第零個位置的值,因此我們要更新的值是 _foreach_i
延伸問題:
針對 LeetCode 29. Divide Two Integers,以下是可能的實作:
EXP6
= ?
EXP7
= ?
延伸問題:
針對 LeetCode 149. Max Points on a Line,以下是可能的實作:
EXP8
= ?
EXP9
= ?
延伸問題:
考慮 ilog32 函式可針對給定的 32 位元無號數,計算扣除開頭的 0,最少需要多少位元才可儲存 (the minimum number of bits required to store an unsigned 32-bit value without any leading zero bits),以下是一個可能的實作:
請問,
EXP10
= ?
EXP11
= ?
延伸問題:
ilog2
實作考慮以下 C++ 二元搜尋樹的程式:
函式 remove_data
嘗試將指定的資料,自給定的二元搜尋樹移除,但原本的 remove_data
過於冗長,於是我們可善用 indirect pointer 改寫為以下:
請問,
EXP12
= ?
EXP13
= ?
EXP14
= ?
首先我們看看這個題目,確定找到該點 p 的 data = d 之後,在 if-else裡主要有三種情況。對應於 if (!q->left) 、 else if (!q->right) 、 else 。
分別為 p 無左子點 、 p 無右子點 、 p 有兩個子點。
首先我們先把簡單的 EXP12 、 EXP13 填上。
(這裡目前在想改 *p 還是 p 比較好)
延伸問題:
考慮以下進行記憶體地址對齊 (alignment) 的巨集:
作用是向上對齊 (roundup)。
請問,
MMM
= ?
NNN
= ?
這題的對齊可以先看第二題的概念。
原來這題是將位置對齊 MAX_ALIGNMENT 的位置,我一直以為是根據 x 來決定對齊的位置,因此一直想不出來。假如是對齊 MAX_ALIGNMENT 就是將 最 LSB 的 4 個 bits 用反相 & 清為 0 。
延伸問題:
ROUND_DOWN
巨集考慮以下巨集可得到二個表示式進行整數除法操作時,最接近的整數值:
注意: 當除數 (即 divisor
) 為負值時,行為沒有定義。
參考執行結果:
DIVIDE_ROUND_CLOSEST(7, 3) = 2
DIVIDE_ROUND_CLOSEST(6, 3) = 2
DIVIDE_ROUND_CLOSEST(5, 3) = 2
請問,
RRR
= ?
SSS
= ?
本來這樣寫就可以了,但是參考別人的想法,因為 c 語言會無條件捨去,所以我們要四捨五入時就要做補正了。 這裡的 (_d >> 1)/_d 之後會變 0.5 ,因此我們先加完再除的時候就等於補正 0.5 的數值了。
延伸問題:
解釋上述程式碼運作原理
在 Linux 核心找出類似的巨集和程式碼,說明 div round-up/closest 的應用場合
考慮以下計算整數開平方根的實作:
i_sqrt
函式的作用等同於 floor(sqrt(x))
請問,
XXX
= ?
YYY
= ?
ZZZ
= ?
我們可以知道 fls() 是找到最少需要儲存的 bits 數(也就是 MSB 的位置),因此初始 m 比 我們的 x 高一個bits,肯定大於 x 。
延伸問題: