来源:https://www.cnblogs.com/intelwisd/p/8424824.html 【导读】:本文详细讲解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&,在某些位保持不变的情况下,某些清零。
/* 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); }
位取反(~)个各位反转,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; }
位异或(^)(相异者或)相异者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;
}
左移(<<)和右移(>>)规则:使操作数的各位左移,低位补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;
/* 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盏灯的状态操作。
需求在此基础上打开从右至左第四盏灯
需求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; }
需求3:从左至右将第三位,第五位关闭分析:原有状态:0101 0101假设状态:1110 1011假设取反:0001 0100只需完成假设取反的状态和原有状态取反即可1左移4位:0001 00001左移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
查看某一位的状态需求从左至右第五位的状态 分析: 原有状态: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 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++) { 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; } 升级:
二进制加密没有上述是否相等问题。 循环移位加密二进制加密: 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 - |
|
来自: 西北望msm66g9f > 《编程》