位运算符

按位与'&'、按位或'|'、按位异或'^'、按位取反'~'

描述:
整数在计算机中用二进制的位来表示,C语言提供一些运算符可以直接操作整数中的位,称为位运算,这些运算符的操作数都必须是整型的。

按位与'&'

按位与运算符‘&’是指:参加运算的两个数据,按二进制位进行“与”运算。如果两个相应的二进制位都为1,则该位的结果值为1;否则为0。这里的1可以理解为逻辑中的true,0可以理解为逻辑中的false。

    0  0  1  1    operand1
    0  1  0  1    operand2
    ----------
    0  0  0  1    (operand1 & operand2) - returned result

Arduino的int类型是一个16位的值,所以使用按位与‘&’运算时会同时对两个int的16位进行操作。
程序片段

    int a =  92;    // in binary: 0000000001011100
    int b = 101;    // in binary: 0000000001100101
    int c = a & b;  // result:    0000000001000100, or 68 in decimal

按位或'|'

按位或运算符'|'是指:两个相应的二进制位中只要有一个为1,该位的结果值为1。借用逻辑学中或运算的话来说就是,一真为真。

   
    0  0  1  1    operand1
    0  1  0  1    operand2
    ----------
    0  1  1  1    (operand1 | operand2) - returned result

程序片段:

    int a =  92;    // in binary: 0000000001011100
    int b = 101;    // in binary: 0000000001100101
    int c = a | b;  // result:    0000000001111101, or 125 in decimal.

示例程序:
对于程序员来说按位与'&'和按位或'|'常见工作是对一个端口进行读 – 写 – 修改。PORTD是个内置的常量,它设置数字引脚0,1,2,3,4,5,6,7的输出状态。如果其中一个位置为1,则该位置的引脚为HIGH。(该引脚需要用命令pinMode ()来设置输出),所以,如果我们写PORTD=B00110001,可以读取到引脚0,4,5的状态为HIGH。

int i;     // 变量计数器
int j;
 
void setup(){
DDRD = DDRD | B11111100; // 设置引脚2-7,留下0和1不变 (xx | 00 == xx)
// 功能与用 pinMode(pin, OUTPUT) 设置引脚 2 - 7一样
Serial.begin(9600);
}
 
void loop(){
for (i=0; i<64; i++){
 
PORTD = PORTD & B00000011;  // 清除引脚2-7状态,留下0和1不变 (xx & 11 == xx)
j = (i << 2);               // 通过左移运算改变引脚2-7状态,留下0和1不变
PORTD = PORTD | j;          // 通过‘|’或运算更改LED的状态
Serial.println(PORTD, BIN); // 调试显示屏蔽
delay(100);
   }
}

按位异或'^'

按位异或运算符'^'是指:参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1

    
    0  0  1  1    operand1
    0  1  0  1    operand2
    ----------
    0  1  1  0    (operand1 ^ operand2) - returned result

简单示例:

    int x = 12;     // binary: 1100
    int y = 10;     // binary: 1010
    int z = x ^ y;  // binary: 0110, or decimal 6

示例程序:
按位异或运算符‘^’通常用于翻转表达式里的一些位数(即1变0,0变1)。

// Blink_Pin_5
// demo for Exclusive OR
void setup(){
DDRD = DDRD | B00100000; // 设置数字引脚5为输出 
Serial.begin(9600);
}
 
void loop(){
PORTD = PORTD ^ B00100000;  // 使其5位翻转 (引脚 5),其他不变
delay(100);
}

按位取反'~'

按位取反运算符'~'是用于求整数的二进制反码,即分别将操作数各二进制位上的1变为0,0变为1。

    0  1    operand1
    ----------
    1  0   ~ operand1
    int a = 103;    // binary:  0000000001100111
    int b = ~a;     // binary:  1111111110011000 = -104

左移运算符'<<'、右移运算符'>>'

描述:
移位运算符(Bitwise Shift)包括左移'«'和右移'»'。左移将一个整数的各二进制位全部左移若干位。

左移运算符'<<'

farm6.staticflickr.com_5587_14710717350_c34e2e41af.jpg
最高两位的11被移出去了,最低两位又补了两个0,其它位依次左移两位。但要注意,移动的位数必须小于左操作数的总位数,比如上面的例子,左边是unsigned int型,如果左移的位数大于等于32位,则结果是Undefined。移位运算符不同于'+ - * / =='等运算符,两边操作数的类型不要求一致,但两边操作数都要做Integer Promotion,整个表达式的类型和左操作数提升后的类型相同。

右移运算符'>>'

farm6.staticflickr.com_5562_14710753819_41e0a784c7.jpg
最低两位的11被移出去了,最高两位又补了两个0,其它位依次右移两位。和左移类似,移动的位数也必须小于左操作数的总位数,否则结果是Undefined。在一定的取值范围内,将一个整数右移1位相当于除以2,小数部分截掉。
当操作数是有符号数时,右移运算的规则比较复杂:
☛如果是正数,那么高位移入0
☛如果是负数,那么高位移入1还是0不一定,这是Implementation-defined的。对于x86平台的gcc编译器,最高位移入1,也就是仍保持负数的符号位,这种处理方式对负数仍然保持了“右移1位相当于除以2”的性质。
综上所述,由于类型转换和移位等问题,用有符号数做位运算是很不方便的,所以,建议只对无符号数做位运算,以减少出错的可能。