位运算符及其相关操作详解

news/2024/11/25 21:55:46/

位运算符详解

前言:由于位运算符是直接对二进制数操作,因此对二进制、八进制、十六进制不甚了解的小伙伴建议先看这篇二进制、八进制、十六进制与十进制的相互关系,这样阅读本篇时将事半功倍

总览

  • 位运算是对计算机存储的二进制序列的相应位进行操作

  • 位运算的操作数必须是整数型或字符型

运算符示例
按位与 &a & b
按位或 |a | b
按位异或 ^a ^ b
按位取反 ~~ a
左移 <<a << b
右移 >>a >> b

按位与 &

  • 按位与是指:参加运算的两个操作数,按二进制对应的位进行“与”运算。如果两个相应的二进制位都为1,则得到的结果为1,否则位0。

  • 0 & 0 = 0,0 & 1 = 0,1 & 1 = 1,1 & 0 = 0,即 全一为1,有零则零

    	int a = 15;//15的二进制序列为 0000 0000 0000 0000 0000 0000 0000 1111int b = 17;//16的二进制序列为 0000 0000 0000 0000 0000 0000 0001 0001int c = a & b;//a和b按位与,那么得到的二进制序列应该是 0000 0000 0000 0000 0000 0000 0000 0001//即十进制的1printf("c = %d\n", c);
    

    在这里插入图片描述

按位或 |

  • 两个相应的二进制位中只要有一个为1,则得到的结果为1。

  • 0 | 0 = 0,0 | 1 = 1,1 | 0 = 1,1 | 1 = 1,即 有一为1,全零为0

    int a = 15;
    //15的二进制序列为 0000 0000 0000 0000 0000 0000 0000 1111
    int b = 17;
    //16的二进制序列为 0000 0000 0000 0000 0000 0000 0001 0001
    int c = a | b;
    //a和b按位与,那么得到的二进制序列应该是 0000 0000 0000 0000 0000 0000 0001 1111
    //即十进制的31
    printf("c = %d\n", c);
    

    在这里插入图片描述

按位异或 ^

  • 参加运算的两个二进制位,如果相同则为0,不同则为1

  • 1 ^ 1 = 0,1 ^ 0 = 1,0 ^ 1 = 1,0 ^ 0 = 0,即 不同为1,相同为0

    int a = 15;
    //15的二进制序列为 0000 0000 0000 0000 0000 0000 0000 1111
    int b = 17;
    //16的二进制序列为 0000 0000 0000 0000 0000 0000 0001 0001
    int c = a ^ b;
    //a和b按位与,那么得到的二进制序列应该是 0000 0000 0000 0000 0000 0000 0001 1110
    //即十进制的30
    printf("c = %d\n", c);
    

    在这里插入图片描述

按位取反 ~

  • 将二进制中的每个位的 0变成1,1变成0

  • 如 ~025就是对八进制数25(即二进制数000 010 101)进行按位取反,得到752(111 101 010)

    int a = 15;
    //15的二进制序列为 0000 0000 0000 0000 0000 0000 0000 1111
    int b = ~ a;
    //a按位取反,二进制序列为 1111 1111 1111 1111 1111 1111 1111 0000
    // 由于在计算机内部是按补码储存,那么转换为反码 1111 1111 1111 1111 1111 1111 1110 1111
    // 转化为原码 1000 0000 0000 0000 0000 0000 0001 0000
    //即十进制的-16
    printf("b = %d\n", b);int c = -1;
    //-1的二进制序列为 1111 1111 1111 1111 1111 1111 1111 1111
    int d = ~c;
    //c按位取反,二进制序列为 0000 0000 0000 0000 0000 0000 0000 0000
    //即十进制的0
    printf("d = %d\n", d);
    

    在这里插入图片描述

左移 <<

  • 左移运算符是用来将一个数的各二进制位全部向左移动若干位,右边的空位补零

  • 例如 a = 3(10) = 011(2),那么 a << 1的结果就是110(2)=6(10)

    int a = 17;
    //a的二进制序列为 0000 0000 0000 0000 0000 0000 0001 0001
    int b = a << 2;
    //b为所有二进制为左移两位得到 0000 0000 0000 0000 0000 0000 0100 0100
    //即十进制数 68
    printf("b = %d\n", b);
    

    在这里插入图片描述

右移 >>

  • 右移运算符是用来将一个数的各二进制位全部向右移动若干位
  • 逻辑右移:右移之后的空位无论原符号位是什么,统一补零
  • 算术右移:如果原符号位位1,那么空位全部补1,反之如果符号位为0,那么空位补0
  • 一般来说,编译器采用的是算术右移
	int a = 17;//a的二进制序列为 0000 0000 0000 0000 0000 0000 0001 0001int b = a >> 2;//b为所有二进制为左移两位得到 0000 0000 0000 0000 0000 0000 0000 0100//即十进制数4int c = -15;//c的二进制原码序列为 1000 0000 0000 0000 0000 0000 0000 1111//反码 1111 1111 1111 1111 1111 1111 1111 0000//补码 1111 1111 1111 1111 1111 1111 1111 0001int d = c >> 2;//如果是逻辑右移//d的二进制序列为 0011 1111 1111 1111 11111 1111 1111 1100//如果是算术右移//d的二进制序列为 1111 1111 1111 1111 1111 1111 1111 1100//反码 1111 1111 1111 1111 1111 1111 1111 1011//原码 1000 0000 0000 0000 0000 0000 0000 0100//即十进制数-4printf("b = %d\n", b);printf("d = %d\n", d);

在这里插入图片描述

位运算的运用

判断一个数是否为偶数

  • Tips:利用位运算对一个数进行奇偶判断的速度要快于if(num % 2 == 0){……}这一操作

  • 我们知道二进制数每一位的权重为2,因此要判断一个数是否为偶数,只需要判断它二进制位的第一位是1还是0

    • 如果第一位是1,那么十进制值就会加上20 = 1这个奇数,而偶数加奇数一定为奇数,因此该数一定是奇数
    • 如果第一位是0,那么这个1就不会加上,该数就一定是偶数
  • 那我们如何得到二进制数的第一位呢?将这个数和1进行按位与操作就可以了,因为1的二进制位只有第一位为1,这样我们就可以单独对二进制的第一位进行判断,从而判断该数的奇偶性

    #include<stdio.h>
    int main()
    {int a;while (scanf_s("%d", &a) != EOF){//注意:关系运算符的优先级高于&,|,^操作符if ((a & 1) == 0)printf("%d是偶数\n",a);elseprintf("%d是奇数\n", a);}return 0;
    }
    

    在这里插入图片描述

统计二进制数中1的个数

方法一

  • 我们知道如果我们要获得十进制数的每一位,我们可以对这个数不断进行模10除以10操作

    • 例如要获得十进制数139的每一位,139 % 10 = 9,得到各位9,139 / 10 = 13,13 % 10 = 3,得到十位3,13 / 10 = 1,1 % 10 = 1,得到百位1
  • 那么对二进制数也可以这样操作,只是将模10除以10改为模2除以2

    int One_Count(unsigned int num)	//参数定义为无符号型是为了确保对负数运算的正确
    {int count = 0;while (num){if (num % 2 == 1)count++;num /= 2;}return count;
    }
    

方法二

  • 可以利用 & 获得二进制的每一位

    int One_Count(int num)
    {int count = 0;for (int i = 0; i < 32; i++){if (((num >> i) & 1) == 1)count++;}return count;
    }
    

方法三(巧解)

  • 可能有小伙伴会认为上面的方法二中的循环固定要循环32次,当计算小数字二进制中1的个数时会浪费时间,那么还有没有更加极致的方法呢?

  • 有!我们来看一个表达式:n = n & (n - 1)

    • 举个例子,n = 15(10) = 1111(2), n - 1 = 14(10) = 1110(2), n = n & (n-1) = 1110(2)

      ​ n = 1110(2), n - 1 = 1101(2), n = n & (n-1) = 1100(2)

      ​ n = 1100(2), n - 1 = 1011(2), n = n & (n-1) = 1000(2)

      ​ n = 1000(2), n - 1 = 0111(2), n = n & (n-1) = 0000(2)

  • 我们可以发现,这个表达式每一次计算,都可以将数n二进制位中最右边的1变成0,因此我们可以创建一个循环,n = n & (n - 1)这个表达式可以运行的次数就是数n二进制位中含1的个数

int One_Count(int num)
{int count = 0;while (num){num = num & (num - 1);count++;}return count;
}

统计两个二进制数中不同位的个数

  • 根据按位异或 ^ 的规律,两个二进制位如果不同,则结果为1,相同结果为0,因此我们可以将这两个数先按位异或,在统计结果二进制位中1的个数

    #include<stdio.h>
    int One_Count(int num)
    {int count = 0;while (num){num = num & (num - 1);count++;}return count;
    }
    int main()
    {int a, b, c;scanf_s("%d %d", &a, &b);c = a ^ b;printf("%d和%d二进制不同位有%d个\n", a,b,One_Count(c));return 0;
    }
    

将一个数用二进制形式打印(左边为高位,右边为地位)

#include<stdio.h>
void Print(int num)
{unsigned int temp = 0x80000000;//即二进制数 1000 0000 0000 0000 0000 0000 0000 0000//第一位不看成符号位while (temp){if ((num & temp))printf("1 ");elseprintf("0 ");temp >>= 1;}
}
int main()
{int a;scanf_s("%d", &a);Print(a);printf("\n");return 0;
}

不创建临时变量交换两个数

  • 这里我们要用到按位异或 ^

  • 这种方法的运行效率不如创建临时变量

    void exchange(int *a, int *b)
    {int temp = *a;*a = *b;*b = temp;
    }
    
  • 这种方法仅作了解即可

    #include<stdio.h>
    int main()
    {int a = 2, b = 3;a = a ^ b;b = a ^ b;a = a ^ b;return 0;
    }
    

http://www.ppmy.cn/news/75085.html

相关文章

AndResGuard 源码解析

背景 抖音包体积优化提出的“键常量池裁剪”是基于腾讯的AndResGuard资源混淆之后做的进一步处理&#xff0c;也就是对resources.arsc文件的处理。而资源混淆&#xff0c;就是对resources.arsc文件进行修改。那么我们可以尝试基于这个思路&#xff0c;对AndResGuard插件源码进…

Linux INPUT 子系统实验

按键、鼠标、键盘、触摸屏都属于输入设备&#xff0c;针对这些设备linux内核提供了一个叫做input的子系统框架来处理输入时间&#xff0c;本质上还是字符设备&#xff0c;只是在此基础上加上了input框架&#xff0c;用户只需要负责上报输入事件&#xff0c;input核心层负责处理…

【考前熟悉】系统集成项目管理师-计算专题

前言 汇总计算专题&#xff1a;加权、沟通渠道、三点估算PERT、净现值、进度网络、挣值分析、预测技术 对照《计算公式汇总》 文章目录 前言计算专题1. 加权算法题2. 沟通渠道3. 三点估算4. 净现值5. 进度网络6. 挣值分析、预测技术 计算专题 1. 加权算法题 在对某项目采购供应…

QGC局域网内连接PX4模拟器JMAVSim

环境 QGroundControl 开源地面站系统; 代码地址: https://github.com/mavlink/qgroundcontrolPX4 开源飞控系统; 代码地址: https://github.com/PX4/PX4-Autopilot QGC可以直接下载运行包. PX4 请根据代码中的说明,进行环境的配置和运行. 通过代码去build地面站和PX4的步骤见官…

对话ChatGPT,大模型时代到来,文末可获得ChatGPT免费访问地址

文章目录 1. 你觉得大模型时代已经到来了吗&#xff1f;2. 大模型和以前的模型有什么区别&#xff1f;3. 列举一下你知道的大模型有哪些4. ChatGPT与传统的智能助手有什么区别&#xff1f;5. ChatGPT有什么优势和劣势&#xff1f;6. ChatGPT有哪些应用场景&#xff0c;请举例说…

魔术表演-第14届蓝桥杯省赛Scratch中级组真题第1题

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第136讲。 魔术表演&#xff0c;本题是2023年5月7日举行的第14届蓝桥杯省赛Scratch图形化编程中级组真题第1题&#xf…

【Linux之进程间通信】05.僵尸进程

【Linux之进程间通信】 项目代码获取&#xff1a;https://gitee.com/chenshao777/linux-processes.git &#xff08;麻烦点个免费的Star哦&#xff0c;您的Star就是我的写作动力&#xff01;&#xff09; 05.僵尸进程 僵尸进程&#xff1a; 指的是进程终止后&#xff0c;资源…

( 动态规划) 115. 不同的子序列 ——【Leetcode每日一题】

❓115. 不同的子序列 难度&#xff1a;困难 给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数。 题目数据保证答案符合 32 位带符号整数范围。 示例 1&#xff1a; 输入&#xff1a;s “rabbbit”, t “rabbit” 输出&#xff1a;3 解释&…