2014年(1)
分类: C/C++
2014-11-20 22:23:55
对于初级的位操作而言,能搞清楚基本的位操作运算符有什么,位操作运算符的优先级是什么样的,对于数据,怎样进行位操作就算可以了。
基本的位操作运算符:AND
(&), OR (|), XOR (^), CPL (~), LF (<<), RF (>>).
位操作的优先级是这样子的:
/************ 优先级表************************/
成员选择&括号运算符 >
单目运算符 > [~]
算术运算符 > [*
/ %] --> [+-]
移位运算符 > [<<
>>]
比较运算符 > [> >= < <=] -->[== !=]
位操作运算符 > [&] --> [^] --> [|]
逻辑运算符 > [&&] --> [||]
三目运算符 >
赋值运算符 >
逗号运算符.
/*********************************************/
单目运算符的特殊性和众所周知的高优先级,大部分人都不会弄错。
双目运算符中逻辑运算符和三目运算符由于包含时间序列点,因此优先级肯定是很低的,所以大部分人也都不会弄错。
插句题外话,包含时间序列点的运算符有:逻辑运算符, 三目运算符,逗号运算符。通常来说,正在这些个双目或者三目运算符中,在先进行计算的操作数的副作用完结之后,才会开始计算另一个操作数,大部分都是先进行左操作数的计算,然后左操作数的副作用,然后有操作数的计算,右操作数的副作用,然后再进行双目运算符的计算,三目运算符也差不多,逻辑运算符有时候(&&的左操作数为假,或者||的左操作数为真)不用计算右操作数,有一些代码刻意的会利用这一点。优先级从高到低,一般你可以认为,出现在时间序列点里面的运算符都是倒数几名优先级。
但是其他双目运算符中与位操作可能一起的运算符以及优先级则很可能会让人疑惑,因此下面的操作式可能经常出现:
A + B << 1 == (A + B) << 1 (算术+移位,容易出现,容易弄错)
!= A +
(B << 1)
A + B & C == (A + B) & C (算术+位运算,容易出现,容易出错)
!= A +
(B & C)
A & B > 0 == A & (B > 0) (位运算+比较,经常出现,容易出错)
!= (A
& B) > 0
A >> 1
> 0 == (A >> 1) > 0 (移位+比较,经常出现,不易出错)
!= A >> (1 > 0)
A ^ B << 1
== A ^ (B << 1) (位运算+移位,经常出现,不易弄错)
!= (A ^ B) << 1
不易出错是因为符合人类思考的习惯,而容易出错则是因为运算符的优先级与人类思考的习惯相悖。在以后的例子中我们会看到。
基本的位操作:get, set, clear, update。
boolean get_bit(int num, int i) /* 获得 bit i of num*/
{
return ((num & 1 << i) != 0);
}
注意 num &
1<< i 不需要加括号,属于不易出错类型,而 (num & 1<< i) != 0 则属于需要加括号的容易出错的类型,不然变成 num & ((1 << j) != 0), 先移位,再比较,再按位与。
int set_bit(int
num, int i) /*将num的bit i置1*/
{
return num | 1 << i;
}
不用加括号的理由同上
int clear_bit(int num, int i) /* 将num的bit i清0*/
{
int mask = ~(1 << i);
return num & mask;
}
int update_bit(int num, int i, int v) /* 将v赋值到num的bit i */
{
int mask = ~(1 << i);
return num & mask | v << i;
}
因为&的优先级高于|,因此首先肯定是v<然后是 num & mask, 最后|。
虽然来说,上面很多地方都不需要加括号,但是因为方便使用代码的人能够更好地阅读代码,通常还是会有一些地方,比如说容易造成误解的地方加上括号,让代码更加有可读性。例如 num & mask | v << i -->(num & mask) | (v << i)。
初级的位运算就到这里,中级的位运算continuing。