前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C/C++ 位操作详解

C/C++ 位操作详解

作者头像
C语言与CPP编程
发布2021-01-12 11:00:53
6260
发布2021-01-12 11:00:53
举报
文章被收录于专栏:c语言与cpp编程c语言与cpp编程

来源:https://www.cnblogs.com/intelwisd/p/8424824.html

【导读】:本文详细讲解C/C++位操作的原理与实际应用,非常值得学习。


位操作(Bit Operation)

位操作与逻辑操作

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

数据的二进制形式表示

8位二进制数据的补码

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

代码语言:javascript
复制
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&,在某些位保持不变的情况下,某些清零。

代码语言:javascript
复制
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("\n------------------------------\n");
    dis32bin(a&b);
    int c = a&b;
    printf("c = %d\n",c);

    return 0;
}
代码语言:javascript
复制
/*
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
*/
代码语言:javascript
复制
按位或|(或)

只有两个都为0时才为0,其余为1.

跟1按位或置1,非0跟0或保持不变,0跟0或为0.

性质:用0|,在某些位保持不变的情况下,某些置1.

代码语言:javascript
复制
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("\n-----------------------------------------\n");
    dis32bin(a|b);
    int c = a|b;
    printf("a|b = %d\n",c);
}
代码语言:javascript
复制
/*
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

按位取反,用于间接的构造某些数据。

代码语言:javascript
复制
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;
}
代码语言:javascript
复制
/*
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^,某些位保持不变的情况下,某些取反

代码语言:javascript
复制
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("\n=============================\n");
    int c = 0;
    dis32bin(a);
    dis32bin(c);
    dis32bin(a^c);
    printf("\n=============================\n");
    int d = 0x0f;
    dis32bin(a);
    dis32bin(d);
    dis32bin(a^d);

    return 0;

}
代码语言:javascript
复制
/*
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^几次幂

代码语言:javascript
复制
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\n",a<<32);
    printf("a<<33 = %d\n",a<<33);
    printf("a<<34 = %d\n",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求模运算。

代码语言:javascript
复制
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("\n===========================\n");
//第二种情况:有符号中的负数:高位补0逻辑右移,高位补1,算数右移。
    int c = 0x800000f0;
    dis32bin(c);
    dis32bin(c>>1);
    dis32bin(c>>2);
    dis32bin(c>>3);

    return 0;
}
代码语言:javascript
复制
/*
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关

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

代码语言:javascript
复制
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:将从左至右第五位关闭

代码语言:javascript
复制
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;
}
代码语言:javascript
复制
/*
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)

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
int main()
{
    int ch = 0x55;
    int mask = 1<<4;
    if(ch&mask)
        printf("此位为1\n");
    else
        printf("此位为0\n");

    return 0;
    //此位为1
}

位操作的总结

1.你要操作的那几位

2.找到合适的掩码

3.找到合适的位运算

test:

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

代码语言:javascript
复制
//先进行掩码的设置操作,之后在位移
int main()
{   
    //0101 0101
    int a = 0x55;
    int mask = a<<3|a<<4|a<<5|a<<6;
    a &= mask;
    a >>= 3;
    printf("a = %d\n",a);
}

//先位移,在进行掩码的设置操作
int main()
{
    int a = 0x55;
    a >>= 3;
    int mask = 0x0f;
    a &= mask;
    printf("a = %d\n",a);

    return 0;
}

优先级

代码语言:javascript
复制
() > 成员运算 > (!) 算术 > 关系 > 逻辑 > 赋值>
() > 成员运算 > (~!) 算术 > 关系 > (>> <<) 位逻辑(& | ^) 逻辑 > 赋值>

循环移位

代码语言:javascript
复制
#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;
}

无参交换

代码语言:javascript
复制
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;
}

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

代码语言:javascript
复制
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\n",buf);
    char ch = 'a';//这种只要传入相同的字符就会出错。
    encode(buf,ch);
    printf("buf = %s\n",buf);
    decode(buf,ch);
    printf("buf = %s\n",buf);

    return 0;
}


int a;
a ^= a;
printf("a = %d\n";a); //自身异或清零。

改进:

代码语言:javascript
复制
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\n",buf);
    char ch = 'a';//这种只要传入相同的字符就会出错。
    encode(buf,ch);
    printf("buf = %s\n",buf);
    decode(buf,ch);
    printf("buf = %s\n",buf);

    return 0;
}

升级:

代码语言:javascript
复制
#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\n",buf);

    char buf2[1024];
    scanf("%s",buf2);
    decode(buf,buf2);
    printf("buf = %s\n",buf);

    return 0;
}

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

循环移位加密

二进制加密:

代码语言:javascript
复制
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;
    }
}

- EOF -

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-01-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 C语言与CPP编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 位操作(Bit Operation)
    • 位操作与逻辑操作
      • 数据的二进制形式表示
        • 按位&(与)
          • 位取反(~)
            • 位异或(^)(相异者或)
              • 左移(<<)和右移(>>)
              • 应用
                • 掩码
                  • 需求在此基础上打开从右至左第四盏灯
                    • 需求2:将从左至右第五位关闭
                    • 分析:
                    • 原有状态:0101 0101
                    • 假设状态:1110 1011
                    • 假设取反:0001 0100
                    • 只需完成假设取反的状态和原有状态取反即可
                    • 1左移4位:0001 0000
                    • 1左移2位:0000 0100
                    • <=> (1<<4) | (1<<2)
                      • test:
                        • 优先级
                          • 循环移位
                            • 无参交换
                              • 异或加密(文本与二进制)
                                • 循环移位加密
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档