在单片机或是工业控制中,位操作是很常见的,利用单片机实现跑马灯的实例就可以通过位操作来实现。C 语言中也能够实现位操作。
位操作符
微操作是针对数据的二进制补码形式的操作,位操作符主要有:
位运算符 | 含义 |
& | 与 |
| | 或 |
~ | 非 |
^ | 异或 |
<< >> | 左移 右移 |
&= |= ^= <<= >>= | 与等 或等 异或等 左移等 右移等 |
位操作
- &:同 1 为 1,反之为 0
- |:同 0 为 0,反之为 1
- ~:0 变 1,1 变 0
- ^:相同为 0,不同为 1
- <<:各位左移,低位补 0,高位溢出
- >>:各位右移,低位舍弃,无符号数和有符号正数高位补 0,有符号负数则根据系统不同补 0 或补 1(0 逻辑右移,1 算术右移)
实现二进制输出
可以用下边的类似程序实现二进制的输出:
#include <stdio.h>
#define LENGTH 16
typedef short DATA;
void bin_print(DATA a)
{
DATA n = LENGTH;
for (DATA i = n-1;i >= 0;i--)
{
printf("%d",(a>>i)&1);
if (!(i%8))
putchar(32);
}
}
int main()
{
DATA ch = 10000;
bin_print(ch);
return 0;
}
掩码
单片机中总是要把某些位清零或置位,这些是用掩码实现的,掩码可以实现:
- 置位
- 清零
- 反转
- 查看
比如下面的程序:
#include <stdio.h>
void bin_print(char a)
{
int n = 8;
for (int i = n-1;i >= 0;i--)
printf("%d",(a>>i)&1);
}
int main()
{
char ch = 1<<6|1<<4|1<<2|1;
char mask = 1<<5|1<<4|1<<3|1<<2;
char test;
bin_print(ch);
putchar(10);
bin_print(mask);
putchar(10);
test = ch&mask;
bin_print(test);
putchar(10);
test = ch|mask;
bin_print(test);
putchar(10);
test = ch^mask;
bin_print(test);
putchar(10);
return 0;
}
结果是:
01010101
00111100
00010100
01111101
01101001
循环移位
上边提到过,左移是高位溢出,低位补 0,逻辑右移是高位补 0,低位舍弃。也可以实现将舍弃的位循环到另外一端:
#include <stdio.h>
#define LENGTH 16
typedef short DATA;
void bin_print(DATA a)
{
DATA n = LENGTH;
for (DATA i = n-1;i >= 0;i--)
{
printf("%d",(a>>i)&1);
if (!(i%8))
putchar(32);
}
}
DATA circle_move(DATA a,DATA n)
{
DATA m = n>0?n:-n;
DATA mask;
while(m--)
mask |= 1<<m;
if (n>0)
a = a<<n | a>>(LENGTH-n);
else
a = a>>(-n) | a<<(LENGTH+n);
return a;
}
int main()
{
DATA ch = 10000;
DATA n = 8;
bin_print(ch);
putchar(10);
bin_print(circle_move(ch,-n));
return 0;
}
无参交换
之前我们说到如果要交换两个参数的值的话,会使用下面的方法:
int a = 3,b = 4,tmp;
tmp = a;
a = b;
b = tmp;
但是上述的方法多出了一个参数,增加了开销。也可以采用下面的方法:
int a = 3,b = 4;
a = a + b;
b = a - b;
a = a - b;
但是上述的方法进行了相加操作,求和容易发生溢出风险。也可以采用下面的方法:
int a = 3,b = 4;
a = a^b;
b = a^b;
a = a^b;
上面的方法利用了一个性质:
x | y | x^y |
0 | 0 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 0 |
从上边的表中可以看出,x,y,x^y 之间可以通过异或实现进行互相转换。