1. 左移<<:将整型数转为二进制补码形式表达后在其末尾添0,再截取需要的低位即可。
分析如下代码(有符号数在左移时会在正负间徘徊):
#include<stdio.h>
int main() {
printf("对于无符号数:\n");
unsigned char a = 0xaa; //无符号整型数a=1010 1010
for (int i = 0; i < 8; i++) {
printf("%d\n", a);
a = a << 1;
}
printf("对于有符号数:\n");
char x = 0xaa; //有符号数 会在正负之间徘徊,由于首位为符号位
for (int i = 0; i < 8; i++) {
printf("%d\n", x);
x = x << 1;
}
return 0;
}
运行结果如图:
分析:对于无符号unsigned char a(有符号char x)=1010 1010(初始a=170,x=-86),其左移一位<<1变化过程为:1 0101 0100(a=84,x=84),10 1010 1000(a=168,x=-88),101 0101 0000(a=80,x=80),1010 1010 0000(a=160,x=-96),1 0101 0100 0000(a=64,x=64),10 1010 1000 0000(a=128,x=-128),101 0101 0000 0000(a=x=0)。由于有符号数首位代表符号位(除了char a=128为a=1000 0000=-128,首位既是符号位又是数值位),因此会在正负之间徘徊。
附加:对于如下代码,当int a=1=0000 0000 0000 0000 0000 0000 0000 0001时,不断地左移1位在末尾补0就意味着不断地给该数*2直到到达整型数据的最小负数1000 0000 0000 0000 0000 0000 0000 0000(首位既是符号位又是数值位)。
2. 右移>>:对于无符号数:右移=在其二进制形式最左边添0;有符号数:在其二进制补码形式左端添符号位!!再截取需要的低位即可。
对于无符号数而言:右移一位相当于十进制形式/2;左移一位相当于十进制形式*2。
3. 典型例题之求二进制数中有几个1。相信大家都见过的做法就是:i=i&(i-1);而求二进制形式中有几个0则是i=i|(i+1)。如下面的代码:
#include<stdio.h>
int Get_0_num(int x) {
int sum0 = 0;
while (x+1 != 0) {
//循环终止条件x+1!=0:通过或运算(1||任何数=1)将该数的每一位0都变成了1,全1状态下即为计算结束。
x = x | (x + 1);
sum0++;
}
return sum0;
}
int Get_1_num(int x) {
int sum1 = 0;
while (x != 0) {
//通过与运算(0&&任何数=0)将该数的1逐渐变为0,最终该数为0,停止计算。
x = x & (x - 1);
sum1++;
}
return sum1;
}
int main() {
int a = 0x12345678; //a=0001 0010 0011 0100 0101 0110 0111 1000
int num1 = Get_1_num(a); //13个1
int num0 = Get_0_num(a); //19个0
printf("num_1:%d\nnum_0:%d\n", num1,num0);
return 0;
}
第二种做法:通过x=x>>1一位一位判断最终得二进制中有几个1(只适用于有符号数二进制首位为0或无符号数!)
#include<stdio.h>
int Get_1_num(int x) {
int num = 0;
while (x) {
if (x & 0x1) {
//通过x&0x1从低位开始一位一位判断:x&0x1=0则最低位=0,x>>1判断下一位。
num += 1; //当x&0x1!=0:即该最低位为1,num++
}
x = x >> 1;//每判断一次右移一位,直到移到x=0判断完毕退出循环
}
return num;
}
int main() {
int a = 0x12345678; //a=0001 0010 0011 0100 0101 0110 0111 1000
int num = Get_1_num(a); //13个1
printf("num_1:%d\n", num);
return 0;
}
3. 异或^
(1)异或运算符运算法则:两个二进制数(补码形式)进行比较:相同为0,相异为1。
#include<stdio.h>
int main() {
char a = 0x89;//1000 1001
char b = 0x76;//0111 0110
char c = a ^ b;//1111 1111=-1(对于有符号数而言:全1=最大的负数-1)
printf("%d\n", c);
return 0;
}
由于为有符号数char类型,故异或结果:1111 1111=最大的负数-1(1111 1111=255还是-1取决于其为有符号数还是无符号数。一切数据在计算机中以补码形式存放!-1:原码1000 0001,补码:1111 1111)
(2)性质:a^(b^c)=(a^b)^c(可结合律),任何二进制数:a^a=0(相同为0,相异为1),a^0=a,a^b^a=b
(3)利用异或运算符交换两个数(利用性质)
#include<stdio.h>
int main() {
int a = 10,b = 20;
a = a ^ b;
b = a ^ b; //b=a^b=(a^b)^b=a^0=a
a = a ^ b; //a=a^b=a^(a^b)=0^b=b
printf("a=%d,b=%d\n", a, b);
return 0;
}