本節書摘來自異步社群《嵌入式linux與物聯網軟體開發——c語言核心深度解析》一書中的第2章,第2.7節,作者朱有鵬 , 張先鳳,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。
在linux核心源碼中有很多函數,你一層一層地檢視進去,會發現其最終實作其實是一些宏構成的。本節舉幾個用宏實作位運算的例子。
用宏定義将一個32位二進制數x的第n位(從右邊起算,也就是bit0算第1位)置位。
顯然,這個宏含有兩個參數,即x和n,是以其模型為 #define set_bit_n(x,n) xxx。
對其某一位置位,我們可以将該位和1相或,其他位和0相或即可,是以得到x | (1<<(n-1))。
是以該宏為#define set_bit_n(x,n) ((x) | (1<<((n)-1)))。
用宏定義将一個32位二進制數x的第n位(右邊起算,也就是bit0算第1位)清零。
顯然,這個宏含有兩個參數,即x和n,是以其模型為 #define clr_bit_n(x,n) xxx。
對其某一位清零,我們可以将該位和0相與,其他位和1相與即可,是以得到x & ~(1<<(n-1))。
是以該宏為#define clr_bit_n(x,n) ((x) & ~(1<<((n)-1)))
這個宏比較複雜,我們單獨拿出來分析它。相信有了上面幾節的學習,了解起來也不會難。該宏實作的是截取指定的連續位(n~m)作為一個新的值。例如變量0x88,也就是0b10001000,若截取第2~4位(bit0為第一位),則值為0b100 = 4。
我們看到上面這麼一個複雜的宏怎麼分析呢?提取對應的括号,将對應的括号分離出來,從最裡邊開始分析,然後将最裡邊視為一個整體,一層一層地向外邊擴充分析。
分析:((x & ~(~(0u)<<(m-n+1))<<(n-1)) >> (n-1)) 提取最裡邊的括号對便是~(0u)<<(m-n+1),然後一層一層地往外面分析,如下所示。

到目前,已經構造出來了bitn~bitm連續為1,其餘位都為0的數。由前面的幾節可知,将這個數與操作數x相與即可從操作數x截取到bitn~bitm位為原數不變,其餘位全為0的數。假設該數為y。
然後隻要再将y右移位(n-1),即可得到以bitn~bitm構成的新數。
課後題
1.嵌入式系統中常常要求使用者對變量或者寄存器進行位操作,下面的函數分别用于設定和清除變量a的第5位,請使用下面宏定義bit5,按要求對變量a進行相應的處理,在函數set_bit5中,用位或指派操作(|=)設定變量a的第5位。在函數clear_bit5中,用位與指派操作(&=)清除變量a的第5位。(軟考題)
static int a;
void set_bit(void)
{
}
void clear_bit5(void)
2.請描述如下位操作的作用。
a |= (1 << 3);
a |= (0b11111 << 3); 或a |= (~((~0) << 5) << 3);
a &= ~(1 << 15);
a &= ~(0b111111111 << 15); 或a &= ~(~((~0) << 9) << 15);
a &= (0b111111 << 3); 或a &= (~((~0) << 6) << 3);
3.請解釋如下兩個宏的含義。
#define set_nth_bit(x, n) (x | ((1u)<<(n-1)))
#define clear_nth_bit(x, n) (x & ~((1u)<<(n-1)))
4.截取變量的部分連續位,例如變量0x88, 也就是10001000,若截取第2~4位,則值為010 = 2,最右邊從第0位算起,假設m=4,n=2。
本文僅用于學習和交流目的,不代表異步社群觀點。非商業轉載請注明作譯者、出處,并保留本文的原始連結。