搜索
查看: 1063|回复: 0

[分享] C/C++位操作的原理与实际应用

[复制链接]

该用户从未签到

2586

主题

2613

帖子

0

蝴蝶豆

版主

最后登录
2021-3-16
发表于 2021-3-5 20:22:38 | 显示全部楼层 |阅读模式
【导读】:本文详细讲解C/C++位操作的原理与实际应用,非常值得学习。

以下是正文

位操作(Bit Operation)

位操作与逻辑操作

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

数据的二进制形式表示8位二进制数据的补码

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

  1. void dis32bin(int data)
  2. {
  3.     int i = 32;
  4.     while(i--)
  5.     {
  6.         if(data & (1<<i))
  7.             printf("1");
  8.         else
  9.             printf("0");
  10.         if(i%4 == 0)
  11.         {
  12.             if(i%8 == 0)
  13.                 printf(" ");
  14.             else
  15.                 printf("-");
  16.         }
  17.     }
  18.     putchar(10);
  19. }

  20. int main()
  21. {
  22.     int a = 0xffffffff;//-1内存中的形式
  23.     dis32bin(a);

  24.     return 0;
  25. }
复制代码
按位&(与)

同1为1,否则为0。

非1跟1按位与保持不变,1跟1按位与为1,跟0按位与清零。

性质:用1&,在某些位保持不变的情况下,某些清零。

  1. void dis32bin(int data)
  2. {
  3.     int i = 32;
  4.     while(i--)
  5.     {
  6.         if(data & (1<<i))
  7.             printf("1");
  8.         else
  9.             printf("0");
  10.         if(i%4 == 0)
  11.         {
  12.             if(i%8 == 0)
  13.                 printf(" ");
  14.             else
  15.                 printf("-");
  16.         }
  17.     }
  18.     putchar(10);
  19. }

  20. int main()
  21. {
  22.     //int a = 0xffffffff;//-1内存中的形式
  23.     int a = 3;
  24.     int b = 11;
  25.     dis32bin(a);
  26.     dis32bin(b);
  27.     printf("\n------------------------------\n");
  28.     dis32bin(a&b);
  29.     int c = a&b;
  30.     printf("c = %d\n",c);

  31.     return 0;
  32. }
复制代码

  1. /*
  2. 0000-0000 0000-0000 0000-0000 0000-0011
  3. 0000-0000 0000-0000 0000-0000 0000-1011

  4. ---------------------------------------
  5. 0000-0000 0000-0000 0000-0000 0000-0011
  6. c = 3
  7. */
复制代码
按位或|(或)

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

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

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

  1. void dis32bin(int data)
  2. {
  3.     int i = 32;
  4.     while(i--)
  5.     {
  6.         if(data & (1<<i))
  7.             printf("1");
  8.         else
  9.             printf("0");
  10.         if(i%4 == 0)
  11.         {
  12.             if(i%8 == 0)
  13.                 printf(" ");
  14.             else
  15.                 printf("-");
  16.         }
  17.     }
  18.     putchar(10);
  19. }
  20. int main() {
  21.     int a = 3;
  22.     int b = 9;
  23.     dis32bin(a);
  24.     dis32bin(b);
  25.     printf("\n-----------------------------------------\n");
  26.     dis32bin(a|b);
  27.     int c = a|b;
  28.     printf("a|b = %d\n",c);
  29. }
复制代码

  1. /*
  2. 0000-0000 0000-0000 0000-0000 0000-0011
  3. 0000-0000 0000-0000 0000-0000 0000-1001

  4. -----------------------------------------
  5. 0000-0000 0000-0000 0000-0000 0000-1011
  6. a|b = 11
  7. */
复制代码

  • 位取反(~)
个各位反转,1->0,0->1

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

  1. void dis32bin(int data)
  2. {
  3.     int i = 32;
  4.     while(i--)
  5.     {
  6.         if(data & (1<<i))
  7.             printf("1");
  8.         else
  9.             printf("0");
  10.         if(i%4 == 0)
  11.         {
  12.             if(i%8 == 0)
  13.                 printf(" ");
  14.             else
  15.                 printf("-");
  16.         }
  17.     }
  18.     putchar(10);
  19. }
  20. int main()
  21. {
  22.     int a = 0x55;
  23.     dis32bin(a);
  24.     dis32bin(~a);//a本身并未发生变化

  25.     dis32bin(0);
  26.     dis32bin(~0);

  27.     return 0;
  28. }
复制代码

  1. /*
  2. 0000-0000 0000-0000 0000-0000 0101-0101
  3. 1111-1111 1111-1111 1111-1111 1010-1010
  4. 0000-0000 0000-0000 0000-0000 0000-0000
  5. 1111-1111 1111-1111 1111-1111 1111-1111
  6. */
复制代码
位异或(^)(相异者或)

相异者1,相同者0

跟1按位异或取反,跟0按位异或保持不变。

性质:用1^,某些位保持不变的情况下,某些取反

  1. void dis32bin(int data)
  2. {
  3.     int i = 32;
  4.     while(i--)
  5.     {
  6.         if(data & (1<<i))
  7.             printf("1");
  8.         else
  9.             printf("0");
  10.         if(i%4 == 0)
  11.         {
  12.             if(i%8 == 0)
  13.                 printf(" ");
  14.             else
  15.                 printf("-");
  16.         }
  17.     }
  18.     putchar(10);
  19. }
  20. int main()
  21. {
  22.     int a = 0x55;
  23.     int b = 0xff;
  24.     dis32bin(a);
  25.     dis32bin(b);
  26.    
  27.     dis32bin(a^b);
  28.     printf("\n=============================\n");
  29.     int c = 0;
  30.     dis32bin(a);
  31.     dis32bin(c);
  32.     dis32bin(a^c);
  33.     printf("\n=============================\n");
  34.     int d = 0x0f;
  35.     dis32bin(a);
  36.     dis32bin(d);
  37.     dis32bin(a^d);

  38.     return 0;

  39. }
复制代码

  1. /*
  2. 0000-0000 0000-0000 0000-0000 0101-0101
  3. 0000-0000 0000-0000 0000-0000 1111-1111
  4. 0000-0000 0000-0000 0000-0000 1010-1010

  5. =============================
  6. 0000-0000 0000-0000 0000-0000 0101-0101
  7. 0000-0000 0000-0000 0000-0000 0000-0000
  8. 0000-0000 0000-0000 0000-0000 0101-0101

  9. =============================
  10. 0000-0000 0000-0000 0000-0000 0101-0101
  11. 0000-0000 0000-0000 0000-0000 0000-1111
  12. 0000-0000 0000-0000 0000-0000 0101-1010
  13. */
复制代码
左移(<<)和右移(>>)

规则:使操作数的各位左移,低位补0,高位溢出。

移位大于32位时,对32求模运算.

左移不溢出的情况下:数字左移相当于乘以2^几次幂

  1. void dis32bin(int data)
  2. {
  3.     int i = 32;
  4.     while(i--)
  5.     {
  6.         if(data & (1<<i))
  7.             printf("1");
  8.         else
  9.             printf("0");
  10.         if(i%4 == 0)
  11.         {
  12.             if(i%8 == 0)
  13.                 printf(" ");
  14.             else
  15.                 printf("-");
  16.         }
  17.     }
  18.     putchar(10);
  19. }
  20. int main()
  21. {
  22.     int a = 0x01;
  23.     dis32bin(a);
  24.     dis32bin(a<<1);
  25.     dis32bin( (a<<31)+1 << 1);
  26. /*
  27. 0000-0000 0000-0000 0000-0000 0000-0001
  28. 0000-0000 0000-0000 0000-0000 0000-0010
  29. 0000-0000 0000-0000 0000-0000 0000-0010
  30. */

  31.     dis32bin(a<<32);
  32.     dis32bin(a<<33);
  33.     dis32bin(a<<34);
  34.     printf("a<<33 = %d\n",a<<32);
  35.     printf("a<<33 = %d\n",a<<33);
  36.     printf("a<<34 = %d\n",a<<34);
  37. /*
  38. 0000-0000 0000-0000 0000-0000 0000-0001
  39. 0000-0000 0000-0000 0000-0000 0000-0010
  40. 0000-0000 0000-0000 0000-0000 0000-0100
  41. a<<33 = 1
  42. a<<33 = 2
  43. a<<34 = 4
  44. */   
  45.     return 0;
  46. }
复制代码

右移(>>)

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

高位:

1.对无符号数和有符号中的整数补0;

2.有符号数中的负数,取决于所使用的系统;补0的称为逻辑右移,补1的称为算数右移
在不溢出的情况下,数字左移相当于除以2^几次幂

同样大于32时对32求模运算。

  1. void dis32bin(int data)
  2. {
  3.     int i = 32;
  4.     while(i--)
  5.     {
  6.         if(data & (1<<i))
  7.             printf("1");
  8.         else
  9.             printf("0");
  10.         if(i%4 == 0)
  11.         {
  12.             if(i%8 == 0)
  13.                 printf(" ");
  14.             else
  15.                 printf("-");
  16.         }
  17.     }
  18.     putchar(10);
  19. }
  20. int main()
  21. {
  22. //第一种情况:无符号数和有符号整数,高位补0,低位舍弃
  23.     unsigned int a = 0x55;
  24.     dis32bin(a);
  25.     dis32bin(a>>4);
  26.     int b = 1;
  27.     dis32bin(b);
  28.     dis32bin(b>>1);
  29. printf("\n===========================\n");
  30. //第二种情况:有符号中的负数:高位补0逻辑右移,高位补1,算数右移。
  31.     int c = 0x800000f0;
  32.     dis32bin(c);
  33.     dis32bin(c>>1);
  34.     dis32bin(c>>2);
  35.     dis32bin(c>>3);

  36.     return 0;
  37. }
复制代码

  1. /*
  2. 0000-0000 0000-0000 0000-0000 0101-0101
  3. 0000-0000 0000-0000 0000-0000 0000-0101
  4. 0000-0000 0000-0000 0000-0000 0000-0001
  5. 0000-0000 0000-0000 0000-0000 0000-0000

  6. =======================================
  7. 1000-0000 0000-0000 0000-0000 1111-0000
  8. 1100-0000 0000-0000 0000-0000 0111-1000
  9. 1110-0000 0000-0000 0000-0000 0011-1100
  10. 1111-0000 0000-0000 0000-0000 0001-1110
  11. */
复制代码
应用

掩码

用一个状态模拟8盏灯的状态操作。

0x55 = 0101 0101,1开 0关
需求在此基础上打开从右至左第四盏灯

  1. void dis32bin(int data)
  2. {
  3.     int i = 32;
  4.     while(i--)
  5.     {
  6.         if(data & (1<<i))
  7.             printf("1");
  8.         else
  9.             printf("0");
  10.         if(i%4 == 0)
  11.         {
  12.             if(i%8 == 0)
  13.                 printf(" ");
  14.             else
  15.                 printf("-");
  16.         }
  17.     }
  18.     putchar(10);
  19. }

  20. int main()
  21. {
  22.     //0101 0101,
  23.     int ch = 0x55;
  24.     int mask = 1<<3;//从本身位置开始移动
  25.     dis32bin(ch);
  26.     ch = ch | mask;
  27.     dis32bin(ch);
  28.     /*
  29. 0000-0000 0000-0000 0000-0000 0101-0101
  30. 0000-0000 0000-0000 0000-0000 0101-1101   
  31.     */
  32.     return 0;
  33. }
复制代码
需求2:将从左至右第五位关闭

  1. int main()
  2. {
  3. // 0101 0101
  4. // 1110 1111  求&运算即可
  5. //~0001 0000
  6.     int ch = 0x55;
  7.     int mask = ~(1<<4);
  8.     dis32bin(ch);
  9.     dis32bin(mask);
  10.     ch = ch & mask;
  11.     dis32bin(ch);

  12.     return 0;
  13. }
复制代码

  1. /*
  2. 0000-0000 0000-0000 0000-0000 0101-0101
  3. 1111-1111 1111-1111 1111-1111 1110-1111
  4. 0000-0000 0000-0000 0000-0000 0100-0101

  5. */
复制代码
需求3:从左至右将第三位,第五位关闭

分析:
原有状态:0101 0101

假设状态:1110 1011

假设取反:0001 0100

只需完成假设取反的状态和原有状态取反即可

1左移4位:0001 0000

1左移2位:0000 0100

<=> (1<<4) | (1<<2)

  1. int main()
  2. {
  3.     int ch = 0x55;
  4.     int mask = ~ ( (1<<4) | (1<<3) );
  5.     //ch = ch & mask;
  6.     ch &= mask;
  7.     dis32bin(ch);

  8.     return 0;

  9.     //0000-0000 0000-0000 0000-0000 0100-0001
  10. }
复制代码
需求4:从左至右第三位到第六位反转

分析:

原有状态:0101 0101 ^异或运算

假设状态:0011 1100

目标状态:0110 1001

假设状态:0010 0000

假设状态:0001 0000

假设状态:0000 1000

假设状态:0000 0100

最终状态:0011 1100

  1. int main()
  2. {
  3.     int ch = 0x55;
  4.     int mask = (1<<5)|(1<<4)|(1<<3)|(1<<2);
  5.     dis32bin(ch);
  6.     ch ^= mask;
  7.     dis32bin(mask);
  8.     dis32bin(ch);
  9.     return 0;
  10. /*
  11. 0000-0000 0000-0000 0000-0000 0101-0101
  12. 0000-0000 0000-0000 0000-0000 0011-1100
  13. 0000-0000 0000-0000 0000-0000 0110-1001
  14. */
  15. }
复制代码
查看某一位的状态

需求从左至右第五位的状态

分析:

原有状态:0101 0101

假设状态:0001 0000 求 & =1

  1. int main()
  2. {
  3.     int ch = 0x55;
  4.     int mask = 1<<4;
  5.     if(ch&mask)
  6.         printf("此位为1\n");
  7.     else
  8.         printf("此位为0\n");

  9.     return 0;
  10.     //此位为1
  11. }
复制代码

位操作的总结

1.你要操作的那几位

2.找到合适的掩码


3.找到合适的位运算
test:

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

  1. //先进行掩码的设置操作,之后在位移
  2. int main()
  3. {   
  4.     //0101 0101
  5.     int a = 0x55;
  6.     int mask = a<<3|a<<4|a<<5|a<<6;
  7.     a &= mask;
  8.     a >>= 3;
  9.     printf("a = %d\n",a);
  10. }

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

  19.     return 0;
  20. }
复制代码
优先级

  1. () > 成员运算 > (!) 算术 > 关系 > 逻辑 > 赋值>
  2. () > 成员运算 > (~!) 算术 > 关系 > (>> <<) 位逻辑(& | ^) 逻辑 > 赋值>
复制代码
循环移位

  1. #include<stdio.h>
  2. void dis32bin(int data)
  3. {
  4.     int i = 32;
  5.     while(i--)
  6.     {
  7.         if(data & (1<<i))
  8.             printf("1");
  9.         else
  10.             printf("0");
  11.         if(i%4 == 0)
  12.         {
  13.             if(i%8 == 0)
  14.                 printf(" ");
  15.             else
  16.                 printf("-");
  17.         }
  18.     }
  19.     putchar(10);
  20. }

  21. //用无符号的类型,避免了右移补1的问题
  22. void circleMove(unsigned int *pa,int n)
  23. {
  24.     n %= 32;
  25.     if(n>0)//左移
  26.         *pa = (*pa<<n) | (*pa>>(sizeof(*pa)*8-n));
  27.     else//右移逻辑
  28.         *pa = (*pa>>(-n)) | (*pa<<(sizeof(*pa)*8-(-n)));
  29. }
  30. int main()
  31. {
  32.     int a = 0x80000001;//1000***0001

  33.     circleMove(&a,1);
  34.     dis32bin(a);

  35.     return 0;
  36. }
复制代码
无参交换

  1. int mySwap(int *pa,int *pb)
  2. {
  3. //引入第三者
  4.     int t = *pa;
  5.     *pa = *pb;
  6.     *pb = t;
  7. }
  8. //以知两者的和,可以求任何其中之一,有益处的弊端
  9. int mySwap1(int *pa1,int *pb1)
  10. {
  11.     *pa1 = *pa1 + *pb1;
  12.     *pb1 = *pa1 - *pb1;
  13.     *pa1 = *pa1 - *pb1;
  14. }
  15. //x,y,x^y,三者之间两两求异或运算即可得到第三者。和加法的思路一样。
  16. int mySwap2(int *pa2,int *pb2)
  17. {
  18.     *pa2 = *pa2 ^ *pb2;
  19.     *pb2 = *pa2 ^ *pb2;
  20.     *pa2 = *pa2 ^ *pb2;
  21.     /*
  22.     *pa2 ^= *pb2;
  23.     *pb2 ^= *pa2;
  24.     *pa2 ^= *pb2;
  25.     */
  26. }
  27. int main() {
  28.     int a = 3;
  29.     int b = 5;
  30.     mySwap(&a,&b);

  31.     return 0;
  32. }
复制代码
异或加密(文本与二进制)

  1. void encode(char *buf,char ch)
  2. {
  3.     int len = strlen(buf);
  4.     for(int i = 0;i < len;i++)
  5.     {
  6.         buf[i] ^= ch;
  7.     }
  8. }
  9. void decode(char *buf,char ch)
  10. {
  11.     int len = strlen(buf);
  12.     for(int i = 0;i < len;i++)
  13.     {
  14.         buf[i] ^= ch;
  15.     }
  16. }

  17. int main()
  18. {
  19.     char buf[] = "I love C++";
  20.     printf("buf = %s\n",buf);
  21.     char ch = 'a';//这种只要传入相同的字符就会出错。
  22.     encode(buf,ch);
  23.     printf("buf = %s\n",buf);
  24.     decode(buf,ch);
  25.     printf("buf = %s\n",buf);

  26.     return 0;
  27. }


  28. int a;
  29. a ^= a;
  30. printf("a = %d\n";a); //自身异或清零。
复制代码

改进:

  1. void encode(char *buf,char ch)
  2. {
  3.     int len = strlen(buf);
  4.     for(int i = 0;i < len;i++)
  5.     {
  6.         if(buf[i] == ch)
  7.             continue;
  8.         buf[i] ^= ch;
  9.     }
  10. }
  11. void decode(char *buf,char ch)
  12. {
  13.     int len = strlen(buf);
  14.     for(int i = 0;i < len;i++)
  15.     {
  16.         if(buf[i] == ch)
  17.             continue;
  18.         buf[i] ^= ch;
  19.     }
  20. }

  21. int main()
  22. {
  23.     char buf[] = "I love C++";
  24.     printf("buf = %s\n",buf);
  25.     char ch = 'a';//这种只要传入相同的字符就会出错。
  26.     encode(buf,ch);
  27.     printf("buf = %s\n",buf);
  28.     decode(buf,ch);
  29.     printf("buf = %s\n",buf);

  30.     return 0;
  31. }
复制代码

升级:

  1. #include<stdio.h>
  2. void encode(char *buf,char *px)
  3. {
  4.     int len = strlen(buf);
  5.     int n = strlen(px);
  6.     int j = 0;
  7.     for(int i = 0;i < len;i++)
  8.     {
  9.         if(buf[i] == px[j])
  10.             j++;
  11.         else
  12.         {
  13.             buf[i] ^= px[j++];
  14.             if(j == n)
  15.                 j = 0;
  16.         }
  17.     }

  18. }
  19. void decode(char *buf,char *px)
  20. {
  21.     int len = strlen(buf);
  22.     int n = strlen(px);
  23.     int j = 0;
  24.     for(int i = 0;i < len;i++)
  25.     {
  26.         if(buf[i] == px[j])
  27.             j++;
  28.         else
  29.         {
  30.             buf[i] ^= px[j++];
  31.             if(j == n)
  32.                 j = 0;
  33.         }
  34.     }

  35. }

  36. int main()
  37. {
  38.     char buf[] = "i love you";
  39.     char xx[] = "19920415";
  40.     encode(buf,xx);
  41.     printf("buf = %s\n",buf);

  42.     char buf2[1024];
  43.     scanf("%s",buf2);
  44.     decode(buf,buf2);
  45.     printf("buf = %s\n",buf);

  46.     return 0;
  47. }
复制代码

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

循环移位加密

二进制加密:

  1. void encode(char *buf,int n);
  2. void decode(char *buf,int n)
  3. int main()
  4. {
  5.     FILE *pfr = fopen("01.png","rb+");
  6.     if(pfr == NULL)
  7.         exit(-1);

  8.     FILE *pfw = fopen("02.png","wb+");
  9.     if(pfw == NULL)
  10.         exit(-1);
  11. /* 解密
  12.     FILE *pfr = fopen("02.png","rb+");
  13.     if(pfr == NULL)
  14.         exit(-1);

  15.     FILE *pfw = fopen("03.png","wb+");
  16.     if(pfw == NULL)
  17.         exit(-1);

  18. */


  19.     char buf[1024];
  20.     int n;
  21.     while((n = fread(buf,1,1024,pfr)) > 0)
  22.     {
  23.         encode(buf,n);
  24.         //decode(buf,n);解密
  25.         fwrite(buf,1,n,pfw);
  26.     }
  27.     fclose(pfr);
  28.     fclose(pfw);

  29.     return 0;
  30. }
  31. void encode(char *buf,int n)
  32. {
  33.     for(int i = 0;i < n;i++)
  34.     {
  35.         unsigned char ch = buf[i];
  36.         buf[i] = ch<<1 | ch>>7;
  37.     }
  38. }

  39. void decode(char *buf,int n)
  40. {
  41.     for(int i = 0;i < n;i++)
  42.     {
  43.         unsigned char ch = buf[i];
  44.         buf[i] = ch>>1 | ch<<7;
  45.     }
  46. }
复制代码

回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条

Archiver|手机版|小黑屋|论坛-意法半导体STM32/STM8技术社区

GMT+8, 2024-4-20 13:59 , Processed in 1.152318 second(s), 29 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表