zoukankan      html  css  js  c++  java
  • C/C++(位操作)

    位操作(Bit Operation)

    位操作与逻辑操作

    位操作不同于逻辑操作,逻辑操作是一种整体的操作,而位操作是针对内部数据位补码的操作。逻辑操作的世界里只有真假(零与非零),而位操作的世界里按位论真假(1和0)。运算也不相同。

    数据的二进制形式表示

    8位二进制数据的补码

    eg:打印一个32位数据的二进制

    
    void dis32bin(int data)
    {
        int i = 32;
        while(i--)
        {
            if(data & (1<<i))
                printf("1");
            else
                printf("0");
            if(i%4 == 0)
            {
                if(i%8 == 0)
                    printf(" ");
                else
                    printf("-");
            }
        }
        putchar(10);
    }
    
    int main() 
    {
        int a = 0xffffffff;//-1内存中的形式
        dis32bin(a);
    
        return 0;
    }
    
    

    按位&(与)

    同1为1,否则为0。
    非1跟1按位与保持不变,1跟1按位与为1,跟0按位与清零。
    性质:用1&,在某些位保持不变的情况下,某些清零。

    void dis32bin(int data)
    {
        int i = 32;
        while(i--)
        {
            if(data & (1<<i))
                printf("1");
            else
                printf("0");
            if(i%4 == 0)
            {
                if(i%8 == 0)
                    printf(" ");
                else
                    printf("-");
            }
        }
        putchar(10);
    }
    
    int main() 
    {
        //int a = 0xffffffff;//-1内存中的形式
        int a = 3;
        int b = 11;
        dis32bin(a);
        dis32bin(b);
        printf("
    ------------------------------
    ");
        dis32bin(a&b);
        int c = a&b;
        printf("c = %d
    ",c);
    
        return 0;
    }
    /*
    0000-0000 0000-0000 0000-0000 0000-0011
    0000-0000 0000-0000 0000-0000 0000-1011
    
    ---------------------------------------
    0000-0000 0000-0000 0000-0000 0000-0011
    c = 3
    */
    

    按位或|(或)

    只有两个都为0时才为0,其余为1.
    跟1按位或置1,非0跟0或保持不变,0跟0或为0.
    性质:用0|,在某些位保持不变的情况下,某些置1.

    void dis32bin(int data)
    {
        int i = 32;
        while(i--)
        {
            if(data & (1<<i))
                printf("1");
            else
                printf("0");
            if(i%4 == 0)
            {
                if(i%8 == 0)
                    printf(" ");
                else
                    printf("-");
            }
        }
        putchar(10);
    }
    int main() {
        int a = 3;
        int b = 9;
        dis32bin(a);
        dis32bin(b);
        printf("
    -----------------------------------------
    ");
        dis32bin(a|b);
        int c = a|b;
        printf("a|b = %d
    ",c);
    }
    /*
    0000-0000 0000-0000 0000-0000 0000-0011
    0000-0000 0000-0000 0000-0000 0000-1001
    
    -----------------------------------------
    0000-0000 0000-0000 0000-0000 0000-1011
    a|b = 11
    */
    
    

    位取反(~)

    个各位反转,1->0,0->1
    按位取反,用于间接的构造某些数据。

    void dis32bin(int data)
    {
        int i = 32;
        while(i--)
        {
            if(data & (1<<i))
                printf("1");
            else
                printf("0");
            if(i%4 == 0)
            {
                if(i%8 == 0)
                    printf(" ");
                else
                    printf("-");
            }
        }
        putchar(10);
    }
    int main()
    {
        int a = 0x55;
        dis32bin(a);
        dis32bin(~a);//a本身并未发生变化
    
        dis32bin(0);
        dis32bin(~0);
    
        return 0;
    }
    /*
    0000-0000 0000-0000 0000-0000 0101-0101
    1111-1111 1111-1111 1111-1111 1010-1010
    0000-0000 0000-0000 0000-0000 0000-0000
    1111-1111 1111-1111 1111-1111 1111-1111
    */
    
    

    位异或(^)(相异者或)

    相异者1,相同者0
    跟1按位异或取反,跟0按位异或保持不变。
    性质:用1^,某些位保持不变的情况下,某些取反

    void dis32bin(int data)
    {
        int i = 32;
        while(i--)
        {
            if(data & (1<<i))
                printf("1");
            else
                printf("0");
            if(i%4 == 0)
            {
                if(i%8 == 0)
                    printf(" ");
                else
                    printf("-");
            }
        }
        putchar(10);
    }
    int main() 
    {
        int a = 0x55;
        int b = 0xff;
        dis32bin(a);
        dis32bin(b);
        
        dis32bin(a^b);
        printf("
    =============================
    ");
        int c = 0;
        dis32bin(a);
        dis32bin(c);
        dis32bin(a^c);
        printf("
    =============================
    ");
        int d = 0x0f;
        dis32bin(a);
        dis32bin(d);
        dis32bin(a^d);
    
        return 0;
    
    }
    
    /*
    0000-0000 0000-0000 0000-0000 0101-0101
    0000-0000 0000-0000 0000-0000 1111-1111
    0000-0000 0000-0000 0000-0000 1010-1010
    
    =============================
    0000-0000 0000-0000 0000-0000 0101-0101
    0000-0000 0000-0000 0000-0000 0000-0000
    0000-0000 0000-0000 0000-0000 0101-0101
    
    =============================
    0000-0000 0000-0000 0000-0000 0101-0101
    0000-0000 0000-0000 0000-0000 0000-1111
    0000-0000 0000-0000 0000-0000 0101-1010
    */
    

    左移(<<)和右移(>>)

    规则:使操作数的各位左移,低位补0,高位溢出。
    移位大于32位时,对32求模运算.
    左移不溢出的情况下:数字左移相当于乘以2^几次幂

    void dis32bin(int data)
    {
        int i = 32;
        while(i--)
        {
            if(data & (1<<i))
                printf("1");
            else
                printf("0");
            if(i%4 == 0)
            {
                if(i%8 == 0)
                    printf(" ");
                else
                    printf("-");
            }
        }
        putchar(10);
    }
    int main()
    {
        int a = 0x01;
        dis32bin(a);
        dis32bin(a<<1);
        dis32bin( (a<<31)+1 << 1);
    /*
    0000-0000 0000-0000 0000-0000 0000-0001
    0000-0000 0000-0000 0000-0000 0000-0010
    0000-0000 0000-0000 0000-0000 0000-0010
    */
    
        dis32bin(a<<32);
        dis32bin(a<<33);
        dis32bin(a<<34);
        printf("a<<33 = %d
    ",a<<32);
        printf("a<<33 = %d
    ",a<<33);
        printf("a<<34 = %d
    ",a<<34);
    /*
    0000-0000 0000-0000 0000-0000 0000-0001
    0000-0000 0000-0000 0000-0000 0000-0010
    0000-0000 0000-0000 0000-0000 0000-0100
    a<<33 = 1
    a<<33 = 2
    a<<34 = 4
    */    
        return 0;
    }
    
    

    右移(>>)
    规则:使操作数的各位右移,移除低位舍弃。
    高位:

    1.对无符号数和有符号中的整数补0;
    2.有符号数中的负数,取决于所使用的系统;补0的称为逻辑右移,补1的称为算数右移
    在不溢出的情况下,数字左移相当于除以2^几次幂
    同样大于32时对32求模运算。

    void dis32bin(int data)
    {
        int i = 32;
        while(i--)
        {
            if(data & (1<<i))
                printf("1");
            else
                printf("0");
            if(i%4 == 0)
            {
                if(i%8 == 0)
                    printf(" ");
                else
                    printf("-");
            }
        }
        putchar(10);
    } 
    int main()
    {
    //第一种情况:无符号数和有符号整数,高位补0,低位舍弃
        unsigned int a = 0x55;
        dis32bin(a);
        dis32bin(a>>4);
        int b = 1;
        dis32bin(b);
        dis32bin(b>>1);
    printf("
    ===========================
    ");
    //第二种情况:有符号中的负数:高位补0逻辑右移,高位补1,算数右移。
        int c = 0x800000f0;
        dis32bin(c);
        dis32bin(c>>1);
        dis32bin(c>>2);
        dis32bin(c>>3);
    
        return 0;
    }
    /*
    0000-0000 0000-0000 0000-0000 0101-0101
    0000-0000 0000-0000 0000-0000 0000-0101
    0000-0000 0000-0000 0000-0000 0000-0001
    0000-0000 0000-0000 0000-0000 0000-0000
    
    =======================================
    1000-0000 0000-0000 0000-0000 1111-0000
    1100-0000 0000-0000 0000-0000 0111-1000
    1110-0000 0000-0000 0000-0000 0011-1100
    1111-0000 0000-0000 0000-0000 0001-1110
    */
    

    应用

    掩码

    用一个状态模拟8盏灯的状态操作。
    0x55 = 0101 0101,1开 0关

    需求在此基础上打开从右至左第四盏灯

    void dis32bin(int data)
    {
        int i = 32;
        while(i--)
        {
            if(data & (1<<i))
                printf("1");
            else
                printf("0");
            if(i%4 == 0)
            {
                if(i%8 == 0)
                    printf(" ");
                else
                    printf("-");
            }
        }
        putchar(10);
    }
    
    int main()
    {
        //0101 0101,
        int ch = 0x55;
        int mask = 1<<3;//从本身位置开始移动
        dis32bin(ch);
        ch = ch | mask;
        dis32bin(ch);
        /*
    0000-0000 0000-0000 0000-0000 0101-0101
    0000-0000 0000-0000 0000-0000 0101-1101    
        */
        return 0;
    }
    

    需求2:将从左至右第五位关闭

    int main()
    {
    // 0101 0101
    // 1110 1111  求&运算即可
    //~0001 0000
        int ch = 0x55;
        int mask = ~(1<<4);
        dis32bin(ch);
        dis32bin(mask);
        ch = ch & mask;
        dis32bin(ch);
    
        return 0;
    }
    /*
    0000-0000 0000-0000 0000-0000 0101-0101
    1111-1111 1111-1111 1111-1111 1110-1111
    0000-0000 0000-0000 0000-0000 0100-0101
    
    */
    

    需求3:从左至右将第三位,第五位关闭

    分析:
    原有状态:0101 0101
    假设状态:1110 1011
    假设取反:0001 0100
    只需完成假设取反的状态和原有状态取反即可
    1左移4位:0001 0000
    1左移2位:0000 0100
    <=> (1<<4) | (1<<2)

    int main()
    {
        int ch = 0x55;
        int mask = ~ ( (1<<4) | (1<<3) );
        //ch = ch & mask;
        ch &= mask;
        dis32bin(ch);
    
        return 0;
    
        //0000-0000 0000-0000 0000-0000 0100-0001
    }
    
    

    需求4:从左至右第三位到第六位反转

    分析:
    原有状态:0101 0101 ^异或运算
    假设状态:0011 1100
    目标状态:0110 1001

    假设状态:0010 0000
    假设状态:0001 0000
    假设状态:0000 1000
    假设状态:0000 0100

    最终状态:0011 1100

    int main()
    {
        int ch = 0x55;
        int mask = (1<<5)|(1<<4)|(1<<3)|(1<<2);
        dis32bin(ch);
        ch ^= mask;
        dis32bin(mask);
        dis32bin(ch);
        return 0;
    /*
    0000-0000 0000-0000 0000-0000 0101-0101
    0000-0000 0000-0000 0000-0000 0011-1100
    0000-0000 0000-0000 0000-0000 0110-1001
    */
    }
    

    查看某一位的状态

    需求从左至右第五位的状态
    分析:
    原有状态:0101 0101
    假设状态:0001 0000 求 & =1

    int main()
    {
        int ch = 0x55;
        int mask = 1<<4;
        if(ch&mask)
            printf("此位为1
    ");
        else
            printf("此位为0
    ");
    
        return 0;
        //此位为1
    }
    

    位操作的总结

    1.你要操作的那几位
    2.找到合适的掩码
    3.找到合适的位运算

    test:

    从键盘输入一个整数,输出3-6位构成的数(从低位0号开始编号)

    //先进行掩码的设置操作,之后在位移
    int main()
    {   
        //0101 0101
        int a = 0x55;
        int mask = a<<3|a<<4|a<<5|a<<6;
        a &= mask;
        a >>= 3;
        printf("a = %d
    ",a);
    }
    
    //先位移,在进行掩码的设置操作
    int main()
    {
        int a = 0x55;
        a >>= 3;
        int mask = 0x0f;
        a &= mask;
        printf("a = %d
    ",a);
    
        return 0;
    }
    

    优先级

    () > 成员运算 > (!) 算术 > 关系 > 逻辑 > 赋值>
    () > 成员运算 > (~!) 算术 > 关系 > (>> <<) 位逻辑(& | ^) 逻辑 > 赋值>
    

    循环移位

    #include<stdio.h>
    void dis32bin(int data)
    {
        int i = 32;
        while(i--)
        {
            if(data & (1<<i))
                printf("1");
            else
                printf("0");
            if(i%4 == 0)
            {
                if(i%8 == 0)
                    printf(" ");
                else
                    printf("-");
            }
        }
        putchar(10);
    }
    
    //用无符号的类型,避免了右移补1的问题
    void circleMove(unsigned int *pa,int n)
    {
        n %= 32;
        if(n>0)//左移
            *pa = (*pa<<n) | (*pa>>(sizeof(*pa)*8-n));
        else//右移逻辑
            *pa = (*pa>>(-n)) | (*pa<<(sizeof(*pa)*8-(-n)));
    } 
    int main()
    {
        int a = 0x80000001;//1000***0001
    
        circleMove(&a,1);
        dis32bin(a);
    
        return 0;
    }
    

    无参交换

    int mySwap(int *pa,int *pb)
    {
    //引入第三者
        int t = *pa;
        *pa = *pb;
        *pb = t;
    }
    //以知两者的和,可以求任何其中之一,有益处的弊端
    int mySwap1(int *pa1,int *pb1)
    {
        *pa1 = *pa1 + *pb1;
        *pb1 = *pa1 - *pb1;
        *pa1 = *pa1 - *pb1;
    }
    //x,y,x^y,三者之间两两求异或运算即可得到第三者。和加法的思路一样。
    int mySwap2(int *pa2,int *pb2)
    {
        *pa2 = *pa2 ^ *pb2;
        *pb2 = *pa2 ^ *pb2;
        *pa2 = *pa2 ^ *pb2;
        /*
        *pa2 ^= *pb2;
        *pb2 ^= *pa2;
        *pa2 ^= *pb2;
        */
    }
    int main() {
        int a = 3;
        int b = 5;
        mySwap(&a,&b);
    
        return 0;
    }
    

    异或加密(文本与二进制)

    void encode(char *buf,char ch)
    {
        int len = strlen(buf);
        for(int i = 0;i < len;i++)
        {
            buf[i] ^= ch;
        }
    }
    void decode(char *buf,char ch)
    {
        int len = strlen(buf);
        for(int i = 0;i < len;i++)
        {
            buf[i] ^= ch;
        }
    }
    
    int main()
    {
        char buf[] = "I love C++";
        printf("buf = %s
    ",buf);
        char ch = 'a';//这种只要传入相同的字符就会出错。
        encode(buf,ch);
        printf("buf = %s
    ",buf);
        decode(buf,ch);
        printf("buf = %s
    ",buf);
    
        return 0;
    }
    
    
    int a;
    a ^= a;
    printf("a = %d
    ";a); //自身异或清零。
    

    改进:

    void encode(char *buf,char ch)
    {
        int len = strlen(buf);
        for(int i = 0;i < len;i++)
        {
            if(buf[i] == ch)
                continue;
            buf[i] ^= ch;
        }
    }
    void decode(char *buf,char ch)
    {
        int len = strlen(buf);
        for(int i = 0;i < len;i++)
        {
            if(buf[i] == ch)
                continue;
            buf[i] ^= ch;
        }
    }
    
    int main()
    {
        char buf[] = "I love C++";
        printf("buf = %s
    ",buf);
        char ch = 'a';//这种只要传入相同的字符就会出错。
        encode(buf,ch);
        printf("buf = %s
    ",buf);
        decode(buf,ch);
        printf("buf = %s
    ",buf);
    
        return 0;
    }
    

    升级:

    #include<stdio.h>
    void encode(char *buf,char *px)
    {
        int len = strlen(buf);
        int n = strlen(px);
        int j = 0;
        for(int i = 0;i < len;i++)
        {
            if(buf[i] == px[j])
                j++;
            else
            {
                buf[i] ^= px[j++];
                if(j == n)
                    j = 0;
            }
        }
    
    }
    void decode(char *buf,char *px)
    {
        int len = strlen(buf);
        int n = strlen(px);
        int j = 0;
        for(int i = 0;i < len;i++)
        {
            if(buf[i] == px[j])
                j++;
            else
            {
                buf[i] ^= px[j++];
                if(j == n)
                    j = 0;
            }
        }
    
    }
    
    int main()
    {
        char buf[] = "i love you";
        char xx[] = "19920415";
        encode(buf,xx);
        printf("buf = %s
    ",buf);
    
        char buf2[1024];
        scanf("%s",buf2);
        decode(buf,buf2);
        printf("buf = %s
    ",buf);
    
        return 0;
    }
    

    二进制加密没有上述是否相等问题。

    循环移位加密

    二进制加密:

    void encode(char *buf,int n);
    void decode(char *buf,int n)
    int main()
    {
        FILE *pfr = fopen("01.png","rb+");
        if(pfr == NULL)
            exit(-1);
    
        FILE *pfw = fopen("02.png","wb+");
        if(pfw == NULL)
            exit(-1);
    /* 解密
        FILE *pfr = fopen("02.png","rb+");
        if(pfr == NULL)
            exit(-1);
    
        FILE *pfw = fopen("03.png","wb+");
        if(pfw == NULL)
            exit(-1);
    
    */
    
    
        char buf[1024];
        int n;
        while((n = fread(buf,1,1024,pfr)) > 0)
        {
            encode(buf,n);
            //decode(buf,n);解密
            fwrite(buf,1,n,pfw);
        }
        fclose(pfr);
        fclose(pfw);
    
        return 0;
    }
    void encode(char *buf,int n)
    {
        for(int i = 0;i < n;i++)
        {
            unsigned char ch = buf[i];
            buf[i] = ch<<1 | ch>>7;
        }
    }
    
    void decode(char *buf,int n)
    {
        for(int i = 0;i < n;i++)
        {
            unsigned char ch = buf[i];
            buf[i] = ch>>1 | ch<<7;
        }
    }
    
  • 相关阅读:
    🔥低代码音视频开发训练营火热报名中!
    编解码再进化:Ali266 与下一代视频技术
    ICCV 2021口罩人物身份鉴别全球挑战赛冠军方案分享
    提升 RTC 音频体验 从搞懂硬件开始
    只要你有目标,只要你肯努力,成功只是时间问题
    安全感到底来自何方
    工作经验小结
    女人的出路在何方?
    那些以为过去了的
    初出茅庐
  • 原文地址:https://www.cnblogs.com/intelwisd/p/8424824.html
Copyright © 2011-2022 走看看