本节书摘来自异步社区《嵌入式Linux与物联网软件开发——C语言内核深度解析》一书中的第2章,第2.7节,作者朱有鹏 , 张先凤,更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.7 技术升级:用宏定义来完成位运算
在Linux内核源码中有很多函数,你一层一层地查看进去,会发现其最终实现其实是一些宏构成的。本节举几个用宏实现位运算的例子。
2.7.1 直接用宏来置位
用宏定义将一个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)))。
2.7.2 直接用宏来复位
用宏定义将一个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)))
2.7.3 截取变量的部分连续位
这个宏比较复杂,我们单独拿出来分析它。相信有了上面几节的学习,理解起来也不会难。该宏实现的是截取指定的连续位(n~m)作为一个新的值。例如变量0x88,也就是0b10001000,若截取第2~4位(bit0为第一位),则值为0b100 = 4。
define GETBITS(x, n, m) ((x & ~(~(0U)<<(m-n+1))<<(n-1)) >> (n-1))
我们看到上面这么一个复杂的宏怎么分析呢?提取对应的括号,将对应的括号分离出来,从最里边开始分析,然后将最里边视为一个整体,一层一层地向外边扩展分析。
分析:((x & ~(~(0U)<<(m-n+1))<<(n-1)) >> (n-1)) 提取最里边的括号对便是~(0U)<<(m-n+1),然后一层一层地往外面分析,如下所示。
到目前,已经构造出来了bitn~bitm连续为1,其余位都为0的数。由前面的几节可知,将这个数与操作数x相与即可从操作数x截取到bitn~bitm位为原数不变,其余位全为0的数。假设该数为Y。
Y = (x & ~(~(0U)<<(m-n+1))<<(n-1))
然后只要再将Y右移位(n-1),即可得到以bitn~bitm构成的新数。
课后题
1.嵌入式系统中常常要求用户对变量或者寄存器进行位操作,下面的函数分别用于设置和清除变量a的第5位,请使用下面宏定义bit5,按要求对变量a进行相应的处理,在函数set_bit5中,用位或赋值操作(|=)设置变量a的第5位。在函数clear_bit5中,用位与赋值操作(&=)清除变量a的第5位。(软考题)
define BIT5 (0X01<<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。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。