Loading

位操作

数据的表示

进制

对于整数的表示形式有:十进制、二进制、八进制、十六进制。例如:char a= 17, b = 0B00010001, c = 021, d = 0X11; 其实 a, b, c ,d 是都相等的。写个demo验证一下

#include <stdio.h>

int main(int argc, char *argv[])
{
    /*
     * 二进制前缀:  0B 或 0b
     * 八进制前缀:  0 (数字0,不是字母o)
     * 十六进制前缀:0x 或 0X
    */
    char a= 17, b = 0B00010001, c = 021, d = 0X11;

    printf("a: %d\n", a);
    printf("b: %d\n", b);
    printf("c: %d\n", c);
    printf("d: %d\n", d);

    return 0;
}

结果:

其中 char 是一个较小的整型而已,无符号取值范围:0~255,有符号取值范围:-127~127。或许你可能会问:为什么要叫字符型? 因为它主要用用来存储字符的。这个时候你又会有疑问:char 的取值范围是 -127~127,存储又是字符,那么字符就是是整数? 没错,字符就是以整数形式存储的,字符 'A' ~ 字符 'Z' 对应的整数是 65 ~ 90。字符 'a' ~ 字符 'z' 对应的整数是 97 ~ 122。其他对应的字符可以查 ASCII码对照表

注意:不管是什么进制表示,最终在计算机中都是以二进制取运算和存储,因为计算机就是处理0101这样二进制数据,什么十进制、八进制、十六进制仅为了方便开发人员阅读而已


补码

对于char 用二进制表示 0 ~ 127,即 0000 0000 ~ 0111 1111。那么用二进制表示 -127 ~ -1 怎么处理?换一句话说:负数怎么用二进制表示? 补码,补码计算口诀:负数的原码取反,再加1

-127 的在计算机的表示形式:补码,其计算过程如下:

-127的原码:1111 1111 (左边第一位是符号位)
-127的反码:1000 0000 (符号位不变,其他位取反)
-127的补码:1000 0001 (反码的基础上加1)

注意:正数的反码、补码都与原码相同

那么为什么整数要用到补码呢?

  • 可以解决了负数的二进制表示

  • 消除0 和 -0的歧义,总不能一个0有两种表示形式吧
    不能char有 0000 0000 和 1000 0000 表示 0 和 -0

  • 使用补码进行计算

在计算机中整数是如何进行计算的?

使用补码进行计算的,例如:3 - 5 的计算过程,当然你可能口算就知道了,但是对于计算来说没有那么简单。

对于3和-5用char类型就可以存储了。

3的补码: 0000 0011
-5的补码:1111 1011

表示式:3 - 5,对于计算机来说就是加法运算,即3 + (-5),用补码做加法运算结果是:1111 1110(补码),转换成十进制过程,补码-1,即 1111 1101,然后取反(符号位不变),即1000 0010,就是十进制的-2

演绎推算完这个过程,觉得计算机的人类先驱真的NB!!! 冯. 诺伊曼 , 图灵,肯.汤普森,丹尼斯.里奇,...

位操作

位操作是针对整数的。位操作运算肯定需要位操作运算符:

  • ~:按位取反
  • &:按位与
  • |:按位或
  • ^:按位异或

看到上面这些关键词,这不是数字电路里面的知识嘛,这些东西怎么跑这来了,仔细想想,计算机玩的不就是数字电路嘛。

写个demo验证一下位操作运算符:

#include <stdio.h>

int main(int argc, char *argv[])
{
    // ~ :按位取反,即0变1,1变0
    unsigned char a = 0X0f;
    printf("~(0X0f): 0X%02hhx\n\n", ~a);

    // &:按位与,即相同位置的值都1,结果才为1
    unsigned char b = a & 0Xf0;
    unsigned char c = a & 0Xff;
    printf("0X0f & 0Xf0: 0X%02hhx\n", b);
    printf("0X0f & 0Xff: 0X%02hhx\n\n", c);

    // |:按位或,即相同位置的值有一个为1,结果就为1
    unsigned char d = a | 0Xf0;
    unsigned char e = a | 0Xff;
    printf("0X0f | 0Xf0: 0X%02hhx\n", d);
    printf("0X0f | 0Xff: 0X%02hhx\n\n", e);

    // ^: 按位异或,即相同位置的值不相等,结果就为1
    unsigned char f = a ^ 0Xf0;
    unsigned char g = a ^ 0Xff;
    printf("0X0f ^ 0Xf0: 0X%02hhx\n", f);
    printf("0X0f ^ 0Xff: 0X%02hhx\n\n", g);

    return 0;
}

结果:

掩码

通过按位与&操作可以检测某一个位的值是0还是1,例如:0B0000 0101,想检测第1位的值,可以进行这样的运算 --> 0B0000 0101 & 0B0000 0010, 若运算结果为非0值,则说明目标的第1位的值是1

通过按位或|操作可以设置某一个位的值是0还是1,例如:0B0000 0101,想设置第1位的值为1,可以进行这样的运算 --> 0B0000 0101 | 0B0000 0010, 若运算结果为 0B0000 0111,则说明设置成功。

规律总结:

  • 不管该位原来的值是0还是1,它跟0进行&运算,得到的结果都是0,而跟1进行&运算,将保持原来的值不变;

  • 不管该位原来的值是0还是1,它跟1进行|运算,得到的结果都是1,而跟0进行|运算,将保持原来的值不变。

写个代码,验证一下:

#include <stdio.h>

int main(int argc, char *argv[])
{
    // 检测第1位的状态
    unsigned char a = 0B11110010, b = 0B11110000;
    unsigned char mask = 0B00000010;
    printf("0B11110010 & 0B00000010: 0X%02x\n", a & mask);
    printf("0B11110000 & 0B00000010: 0X%02x\n\n", b & mask);

    // 设置第1位的状态
    unsigned char d = 0B11110010, e = 0B11110011;
    printf("0B11110010 | 0B00000010: 0X%02x\n", d | 0B00000010);
    printf("0B11110011 | 0B00000000: 0X%02x\n\n", e | 0B00000000);

    return 0;
}

结果:


移位操作

  • 左移<<: 整体全部向左移动N位

  • 右移>>: 整体全部向右移动N位

直接通过代码验证:

示例一:简单了看1的移动过程

#include <stdio.h>

int main(int argc, char *argv[])
{
    unsigned char a = 0B00000001;
    unsigned char b = 0B10000000;

    // 左移<<
    for (int i = 0; i < 8; i++)
    {
        printf("a左移%d位: 0X%02x\n", i, a << i);
    }
    printf("\n");


    // 右移>>
    for (int i = 0; i < 8; i++)
    {
        printf("b右移%d位: 0X%02x\n", i, b >> i);
    }

    return 0;
}

结果:

示例二:看多个1的移动过程

#include <stdio.h>

int main(int argc, char *argv[])
{
    unsigned char a = 0B00000011;
    unsigned char b = 0B11000000;

    // 左移<<
    for (int i = 0; i < 8; i++)
    {
        printf("a左移%d位: 0X%02hhx\n", i, (unsigned char)a << i);
    }
    printf("\n");


    // 右移>>
    for (int i = 0; i < 8; i++)
    {
        printf("b右移%d位: 0X%02hhx\n", i, b >> i);
    }

    return 0;
}

结果:

目标匹配

有一个uinst32_t的数,需要验证:第8位~第12位是00100,第21位是1。

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>

bool checkBits(uint32_t num, int start, int end, uint32_t pattern) 
{
    uint32_t mask = ((uint32_t)1 << (end - start + 1)) - 1; // 创建掩码
    mask <<= start; // 将掩码移到正确的位置

    // 使用掩码将不需要的位清零,然后与pattern进行比较
    return ((num & mask) >> start) == pattern;
}

int target_check(uint32_t *arr, int cnt)
{
    int rc = 0;
    for (int i = 0; i < cnt; i++)
    {
        uint32_t binaryNumber = arr[i];
        bool isPatternCorrect = checkBits(binaryNumber, 8, 12, 0b00100);
        bool isBit21Set = checkBits(binaryNumber, 21, 21, 0b1);
        if (isPatternCorrect && isBit21Set)
        {
            rc = 0;
            printf("%d --> the value is true, (0X%08x)\n\n", i + 1, binaryNumber);
        }
        else
        {
            rc = 1;
            printf("%d --> the value false , (0X%08x)\n\n", i + 1, binaryNumber);
        }
    }
    return rc;
}

int main(int argc, char *argv[])
{
    /*
     * 0B00000000001000000000010000000000, 从右到左,分别是第0位,第1位,... ,第31位。
     * 可以使用无符号32位的整型存储,  uint32_t num = 0B00000000001000000000010000000000;
     * 32位的二进制可以用十六进制表示,uint32_t num = 0X00200400; 即4位二进制可用1位十六进制表示
     * 
     * 要求检测目标数值的第8位至第12位是00100,第21位是1
     */

    uint32_t arr[] = {
        0X00200400,
        0X01200400,
        0Xab300400,
        0X00100400, // error
        0X00200500, // error
        0X00800300, // error
        0B00000000001000000000010000000000,
        0B00000000001000000001010100000000,
    };
    int cnt = sizeof(arr) / sizeof(arr[0]);
    target_check(arr, cnt);

    return 0;
}

结果:

posted @ 2024-07-07 19:08  eiSouthBoy  阅读(34)  评论(0)    收藏  举报