按位于、或、异或、取反操作小结
最近新冠肺炎肆虐,学校也回不去,呆在家里闲暇无事学嵌入式,将位操作的相关知识点做了一下总结
P.S.
- "&"、"|"、"^"、"~"用作位运算,就是先把操作数展开成二进制、再对每一位进行相应的运算,是按位进行的
- "&&"、"||"用作逻辑运算,就是比较操作数的逻辑真假
- 如常见的判断语句
if( ( a == b ) && ( c == d ) )
,这里的a==b
和c==d
都会返回一个逻辑真假、也就是boolean,之后再对这两个布尔值进行逻辑运算,如true && true
结果为真,true && false
结果为假
- 如常见的判断语句
一、按位与
按位进行与操作,有0则0
-
eg:
- 1、0110 & 0100 = 0100 (有0则0,1 1才1)
- 2、0xf & 0xe = 0xe (即 0x1111 & 0x1110 = 0x1110)
- 3、0xef & 0xfe = 0xee (即 0x1110 1111 & 0x1111 1110 = 0x1110 1110)
-
常见用途:
- 1、取一个数中指定的位:
- 0101 0101取低4位:0101 0101 & 0000 1111 = 0000 0101
- 2、清零:
- 0101 0101 & 0000 0000 = 0000 0000
- 3、判断奇偶:
- 代码段:
- 1、取一个数中指定的位:
//常用方法
for(int i = 0; i < 10; i++)
{
if(i % 2 == 0)
printf("%d%s\n", i, "为偶数");
else
printf("%d%s\n", i, "为奇数");
}
//位与方法(奇数末尾为1,只需将所求数和一个末尾位为1、其他位为全0的数相与即可判断):
for(int i = 0; i < 10; i++)
{
if(i & 1)
printf("%d%s\n", i, "为奇数");
else
printf("%d%s\n", i, "为偶数");
}
二、按位或
按位进行或操作,有1则1
-
eg:
- 1、0110 | 0100 = 0110
- 2、0xf | 0xe = 0xf (即 0x1111 | 0x1110 = 0x1111)
- 3、0xef | 0xfe = 0xff (即 0x1110 1111 | 0x1111 1110 = 0x1111 1111)
-
常见用途:
- 1、将一个数中指定的位置1:
- 0101 0101低4位置1:0101 0101 | 0000 1111 = 0101 1111
- 2、交换高低位:
- 假设有一个16位二进制无符号整数,称其前8位为高位,后8位为低位,如何交换其高低位?
- 思路:
- 1)设此数为a,先将其逻辑右移8位存入a1,则原来的高位变成了低位,原高位补0
- 2)再将a逻辑左移8位存入a2,则原来的低位变成了高位,原低位补0
- 3)最后将a1与a2相或,就将a的高低位交换了
- 4)以下的代码举例将65534交换高低位变成了65279
- 代码:
- 1、将一个数中指定的位置1:
#include "stdio.h"
void DecToBin(int num){
if(num > 0){
DecToBin(num / 2);
printf("%d", num % 2);
}
}
int main(){
unsigned short a = 65534;
unsigned short a_swap = (a>>8) | (a<<8);
DecToBin(a); printf("\n");
DecToBin(a_swap);
printf("\n%d", a_swap);
return 0;
}
三、按位异或
按位进行异或操作,同0异1(即,“异”为1)
- eg:
- 1、0110 ^ 0100 = 0010
- 2、0xf ^ 0xe = 0x1 (即 0x1111 ^ 0x1110 = 0x0001)
- 3、0xef ^ 0xfe = 0x11 (即 0x1110 1111 ^ 0x1111 1110 = 0x0001 0001)
- 常见用途:
- 1、将一个数中指定的位翻转(与0异或保留原值,与1异或翻转):
- 0101 0101低4位翻转:0101 0101 ^ 0000 1111 = 0101 1010
- 2、交换两数(直接使用位操作来实现,免去第三方变量的引入):
- 思路:
- 1)先将a与b异或,结果赋值给a,即
a=a^b
- 2)再将a与b异或,结果赋值给b,这一步是关键,此时有:
b=(a^b)^b=b^(a^b)=b^(b^a)=b^b^a
任何数和自己异或结果为0、任何数和0异或结果为自己的原值,故这一步就将a赋值给了b - 3)最后将a与b异或,结果赋值给a,即
a=a^b
,由前两步的工作知、此时:
a=a^b、b=a,故有a=a^b=(a^b)^b=(a^b)^a=a^(a^b)=a^a^b=b
,
这一步有点绕,读者可以在纸上比划比划。这样就将b赋值给了a,达到了交换两数的目的
- 1)先将a与b异或,结果赋值给a,即
- 代码:
- 思路:
- 1、将一个数中指定的位翻转(与0异或保留原值,与1异或翻转):
#include "stdio.h"
//void Swap(int &a, int &b) {
void Swap(int a, int b) {
if (a != b) {
a ^= b;
b ^= a;
a ^= b;
}
printf("%s0x%x%s0x%x.\n", "交换后,a = ", a, ", b = ", b);
//注:本来交换后的打印语句想写在main函数中,但是c语言不支持将引用“&”作形参,使用指针作形参时位操作又会报错,编者索性将打印语句写在了交换函数的函数体中,这是因为形参不为“&”或“*”时,函数体中对形参的一系列操作在外部看来“无效”
}
int main() {
int a = 110, b = 111;
printf("%s0x%x%s0x%x.\n", "交换前,a = ", a, ", b = ", b);
Swap(a, b);
//printf("%s0x%x%s0x%x.\n", "交换后,a = ", a, ", b = ", b);
return 0;
}
四、按位取反
按位进行取反操作
- eg:
- 1、~0110 = 1001; ~0100 = 1011
- 2、~0xf = 0xfffffff0; ~0xe = 0xfffffff1
- 常见用途:
- 1、在嵌入式中,一个数的最低位常被用作控制某物的开关,可以用取反操作将最低位清零:
a &= ~1
(取反运算符的优先级比算数、关系、逻辑和其他运算符都高) - 2、变换符号(正变负、负变正,只需对二进制按位取反再加1即可):
- 代码:
- 1、在嵌入式中,一个数的最低位常被用作控制某物的开关,可以用取反操作将最低位清零:
#include "stdio.h"
int TransformSign(int a) {
return ~a + 1;
}
int main() {
int a = 0xf;
printf("%s0x%x%s%d.\n", "变换前,a = ", a, " = ", a);
a = TransformSign(a);
printf("%s0x%x%s%d.\n", "变换后,a = ", a, " = ", a);
return 0;
}
- 3、求整数的绝对值(将int右移31位,则int的最高位变成了最低位,这一位就是符号位):
- 代码:
#include "stdio.h"
int abs_1(int a) {
//16位操作系统中,int占16位;32位OS中,int占32位、4字节;64位OS中,int同样占32位,long或long long占64位
int i = a >> 31;
return i == -1 ? (~a + 1) : a;
}
int abs_2(int a) {
int i = a >> 31;
//若a是负数,则i是-1,二进制位0xFFFF,任何数与-1异或相当于取反,而-i是+1,则下列语句表示对负数取反加一得绝对值
//若a是正数,则i是0,任何数与0异或都不变,则下列语句无意义
return ((a ^ i) - i);
}
int main() {
int a = -15;
printf("%s%d.\n", "a的原值为 ", a);
printf("%s%d.\n", "a的绝对值为 ", abs_1(a));
printf("%s%d.\n", "a的绝对值为 ", abs_2(a));
return 0;
}
五、其他应用算法
- 1、二进制逆序:
- 思路:对于一个16位的二进制数(如23333,即0101101100100101),类比归并排序的分组处理
- 1)每两位为一组,组内高低位互换:01 01 10 11 00 10 01 01 --> 10 10 01 11 00 01 10 10
- 2)每四位为一组,组内高低位互换:1010 0111 0001 1010 --> 1010 1101 0100 1010
- 3)每八位为一组,组内高低位互换:10101101 01001010 --> 11011010 10100100
- 4)每十六位为一组,组内高低位互换:11011010 10100100 --> 1010010011011010
- 代码:
- 思路:对于一个16位的二进制数(如23333,即0101101100100101),类比归并排序的分组处理
#include "stdio.h"
void PrintBin(short a) {
int i;
for (i = sizeof(a) * 8 - 1; i >= 0; --i) {
if ((a >> i) & 1)
putchar('1');
else
putchar('0');
if (!(i % 8))
putchar(' ');
}
putchar('\n');
}
int main() {
short a = 233333;
printf("%s", "逆序前,值为:");
PrintBin(a);
printf("%s", "逆序后,值为:");
a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1);
a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2);
a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4);
a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8);
PrintBin(a);
return 0;
}
- 2、