【导读】:本文详细讲解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("\n------------------------------\n");
- dis32bin(a&b);
- int c = a&b;
- printf("c = %d\n",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("\n-----------------------------------------\n");
- dis32bin(a|b);
- int c = a|b;
- printf("a|b = %d\n",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("\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;
- }
复制代码
- /*
- 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\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求模运算。
- 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;
- }
复制代码
- /*
- 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\n");
- else
- printf("此位为0\n");
- 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\n",a);
- }
- //先位移,在进行掩码的设置操作
- int main()
- {
- int a = 0x55;
- a >>= 3;
- int mask = 0x0f;
- a &= mask;
- printf("a = %d\n",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\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); //自身异或清零。
复制代码
改进:
- 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;
- }
复制代码
升级:
- #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;
- }
复制代码
二进制加密没有上述是否相等问题。
循环移位加密
二进制加密:
- 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;
- }
- }
复制代码
|