• C primer plus学习笔记 —— 10、位运算


    概述

    单独操控位,有时向硬件设备发送一两个字节来控制这些设备,其中每个位都有特定含义。

    一个字节:从左到右8位编号分别为7~0。
    编号7被称为高位,编号0被称为低位。
    一字节有8bit,2的8次方256个值。通常无符号1byte能表示的数范围是0~255.有符号表示范围-128 —— +127

    有符号和无符号数

    无符号数

    无符号数都是从0开始,表示的范围是他的2次方位数的最大值。
    所以无符号1byte能表示的数范围是0~255.

    有符号

    如何表示有符号数取决于硬件,而不是某种语言。
    有下面两种表示法,我们对比一下

    高位表示负数法

    最简单方式,我们使用一位表示符号位,所以2的7次方是128。关键在于我们如何表示负数,如果我们使用二进制反码的形式来表示,比如说00000001表示+1,他的取反11111110表示-1.

    这种方式有个缺点:
    这时+0和-0就有两个数了。浪费了一个值的表示。 只能表示则是-127-0和0- +127。范围是-127 - +127。

    二进制反码同理,也会浪费一个数值。

    二进制补码

    为了避免这种情况,有符号数使用二进制补码形式。这也是当今最常用的硬件表示负数方法。
    我们使用取反加一,来确定一个负值。

    1. 表示正数:符号位是0则后7位表示0-+127. 0的表示为00000000。127表示为01111111,+1表示为00000001。和无符号方式相同
    2. 表示负数:我们用正数的取反加一来表示负数。如果符号位设为1,-127表示就是127的取反加1,为10000001。-1表示就是+1的取反加1,11111111. 而0的取反加一表示为将10000000,因为有0了,所以我们将这个数作为最大负值也就是-128,所以有符号位负数表示范围是-128 - -1,而没有0.

    这样使用二进制补码我们就可以表示-128 - + 127的范围的数了。

    位操作

    把某个变量进行位操作,并不是直接修改这个变量的值,而是位操作后返回一个新值,原值不变。
    比如取反运算:
    newVal = ~val;
    如果想改变原值则:val = ~val;

    位操作主要有三种:

    1. 与&, A&B, 有0则为0
    2. 或|,A|B,有1则为1
    3. 异或 ^, 相同为0,不同为1

    移位运算符

    左移

    将所有值向左移动,左边移出的值就不要了,右边用0填充空位。
    比如一个二进制数左移2位
    10001010 << 2 = 00101000;

    用法

    移位运算符主要是针对2的幂次方实现快速乘除法。
    number << n; // number乘以2的n次方

    左移0,不变。左移1,十进制乘2,左移2,十进制乘2的2次方。
    int a = 1;
    a <<= 2; //a = 4;

    右移

    将所有值向右移动,移出的值就不要了,左边用0填充,但是如果是有符号数,则最高位可能填的是1或者0,取决于硬件。
    右移0,十进制不变,右移1,十进制除2,右移2,十进制除2的2次方,右移3,十进制除2的3次方。

    用法

    举例1:
    number >> n; // 如果值为正数,则number除以2的n次方
    int a = 16;
    a >>= 2; //a = 4;

    举例2:
    我们还可以针对十六进制数直接移动,可以更直观

    unsigned long color = 0x002a162f;
    color >>= 8; //0x00002a16。 因为右移8位正好是十六进制两个数表示8位,所以直接将color末尾的2f去掉即可。
    color &= 0xff; //0x16。这时使用&运算符保留位,可以看到我们保留的是十六进制最后两个数,所以得到0x16.
    
    • 1
    • 2
    • 3

    移位负值

    如果移动的值为负值,则这个操作的行为由编译器来决定,他是不确定的。

    a << -5;
    
    • 1

    检查一个数的指定位是否为1,并设置1

    我要检查val的第几位是否为1:肯定要使用&运算符
    这里i是要检查的第几个bit位。

    int val = 355;
    if ( (val >> i) & 1 == 1) {
    }
    
    • 1
    • 2
    • 3

    当我们设置位为1的时候,肯定要使用 | 运算符
    比如我要设置val的第i位为1

    int val = 355;
    val |= (1 << i);
    
    • 1
    • 2

    如果i=4,也就是我们想将val的第四位设置为0,
    那么(1<<4) 表示1左移4位则二进制为 00010000;
    这时val |= (1 << i); 就表示将val的第4个比特位设置为1,其他位保持不变。

    用法汇总

    掩码,保留位(取低八位)&

    掩码指的是设置开关组合,1为开,0为关。
    比如我们设置一个byte的掩码 MASK= 00000010; 这里mask只有1号为是1.
    如果我们想把一个byte的变量flag中的除1号位其他所有位都设置为0。我们可以这样:
    flag = flag & MASK;
    可以简化为flag &= MASK;
    使用&运算符,就可以将flag除1号位的其他位都置为0;
    这就是掩码的含义,将flag中其他所有位都隐藏,只留下我们指定的某个位,这个位在掩码中用1表示,1表示透明,0表示掩盖。
    在这里插入图片描述

    再举个常见例子:
    int a &= 0xff;
    这里我们可以看到掩码宽度为8bit。
    这里的作用:保留变量ch的后八位,(因为16进制一个数表示4bit)。变量a是int类型一共有32位,将前面的24为都置为0,只保留后八位,取低八位的值。

    检查位的值 &

    比如,我要检查一个变量的1号位是否为1.
    这样做

    MASK = 2; //00000010
    if ((flag & MASK) == MASK) {
    	puts("check");
    }
    
    • 1
    • 2
    • 3
    • 4

    检查flag中的1号位是否被设置为1。

    清零位 & ~

    比如MASK= 00000010;
    flag &= ~MASK;
    因为直接使用&=是保留MASK中指定的位,而取反,就是清零MASK中指定的位,保留MASK其他位。

    设置位 |

    可以用来设置某一位的值为1.
    比如MASK= 00000010;
    flag |= MASK;
    目的就是把flag的1号位给设置为1. 其他位保持不变

    切换位 ^

    flag ^= MASK;
    将flag中与MASK对应为1的位都切换,MASK为0的位保持不变。

  • 相关阅读:
    一起Talk Android吧(第三百七十九回:让ViewPager一屏幕显示三页)
    勒索病毒最新变种.halo勒索病毒来袭,如何恢复受感染的数据?
    SpringBoot+Vue实现前后端分离的在线考试报名系统
    Docker中安装Redis
    C++ map容器的简单用法
    Spring接入Metric+Graphite+Grafana搭建监控系统
    【Verilog】时钟奇分频实战经验
    基于nodejs学校宿舍管理系统-计算机毕设 附源码45118
    【MySql】MySQL概述及其语法概述
    window server事件ID说明
  • 原文地址:https://blog.csdn.net/chongbin007/article/details/126358072