嵌入式:按位与、或、异或、取反操作小结

按位于、或、异或、取反操作小结

最近新冠肺炎肆虐,学校也回不去,呆在家里闲暇无事学嵌入式,将位操作的相关知识点做了一下总结
P.S.

  • "&"、"|"、"^"、"~"用作位运算,就是先把操作数展开成二进制、再对每一位进行相应的运算,是按位进行的
  • "&&"、"||"用作逻辑运算,就是比较操作数的逻辑真假
    • 如常见的判断语句if( ( a == b ) && ( c == d ) ),这里的a==bc==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、判断奇偶:
      • 代码段:
//常用方法
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
      • 代码:
#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,达到了交换两数的目的
      • 代码:
#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即可):
      • 代码:
#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
    • 代码:
#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、
发布了16 篇原创文章 · 获赞 7 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/yuecangjiao5151/article/details/104214899
今日推荐