一、补码,反码
正数的源码=反码=补码
负数的反码等于符号位不变,其余位按位取反
负数的补码等于反码+1
二、按位与,按位或,按位异或,按位非
1.按位与 &
0&0=0; 1&0=0; 1&1=1
任意二进制位与0都为0,与1为其本身 (a&0=0 ; a&1=a)
2.按位或 |
0|0=0; 0|1=1; 1|1=1
任意二进制位或1都为1,或0为其本身 (a|0=a ; a|1=1)
3.按位异或 ^
两个二进制位相同为0,不同为1
0^0=0 ; 0^1=1; 1^1=0
任意二进制位a异或1都为非a,异或0为其本身(a^1=!a ; a^0=a)
4.按位非 ~
所有位按位取反(区别于反码的符号位不变)
注意:按位非后的二进制串为补码,读取时要化为原码
int main() {
int a = 0; //补码:00000000 00000000 00000000 00000000
int b = ~a; //补码:11111111 11111111 11111111 11111111 反码:11111111 11111111 11111111 11111110 原码:10000000 00000000 00000000 00000001
printf("%d", b); // 结果为-1
return 0;
}
int main() {
int a = -2; //原码:10000000 00000000 00000000 00000010 反码:11111111 11111111 11111111 11111101 补码:11111111 11111111 11111111 11111110
int b = ~a; //补码:00000000 00000000 00000000 00000001
printf("%d", b); //结果为1
return 0;
}
三、逻辑与,逻辑或
1.逻辑与 &&
左右两边同时为真才是真,有一个为假则为假
5&&3=1; 5&&0=0 ;0&&0=0
任意数逻辑与0都为0(a&&0=0)
2.逻辑或 ||
左右两边有1个为真就是真,两个同时为假则为假
5||3=1 ; 5||0=1 ; 0||0=0
任意数逻辑或1都为1(a||1=1)
注意:计算机在处理a && b && c && d时,只要碰到一个为0,则直接返回结果0
处理a || b || c || d时,只要碰到一个为1,则直接返回1,剩余的数不会再进行运算
int main() {
int i = 0, a = 1, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("%d %d %d %d ", a,b,c,d); //结果为 2 3 3 5
return 0;
}
int main() {
int i = 0, a = 1, b = 2, c = 3, d = 4;
i = a++ || ++b || d++;
printf("%d %d %d %d ", a,b,c,d); //结果为 2 2 3 4 ,++b和d++不参与运算
return 0;
}
四、左移,右移
左移右移不改变数值本身,如果要用位移后的值需要用变量存储
int main() {
int a = 1;
a >> 1;
printf("%d\n", a); //结果仍为1
int b = a >> 1;
printf("%d", b); //结果为0
return 0;
}
1.无符号数左右移
使用逻辑左右移规则:
左移,左端舍弃,右端补0; 右移,右端舍弃,左端补0
a左移n位代表,a=a*2^n; a右移n位代表,a=a/2^n
int main() {
unsigned int a0 = 35; //00000000 00000000 00000000 00100011
unsigned int a1 = a0 << 1; //00000000 00000000 00000000 01000110
unsigned int a2 = a0 >> 1; //00000000 00000000 00000000 00010001
printf("%d %d %d", a0, a1, a2); //35 70 17
return 0;
}
2.有符号数左右移
(1)左移
使用逻辑移位规则:
左端舍弃,右端补0(符号位可能改变)
int main() {
int a = -1073741825; // 原码:11000000 00000000 00000000 00000001 反码:10111111 11111111 11111111 11111110 补码:10111111 11111111 11111111 11111111
int b = a << 1; //补码:01111111 11111111 11111111 11111110 反码:01111111 11111111 11111111 11111110 原码:01111111 11111111 11111111 11111110
printf("%d", b);//输出了2147483646,符号位变了,说明c语言的左移为逻辑左移
return 0;
}
(2)右移
有符号数在计算机中以补码的形式存储,这里的右移是基于补码来讨论的(相当于算数右移中补码的规则)
使用算术移位中补码的规则:
将补码的二进制位右移,右端舍弃,左端补符号位(符号位一定保持不变)
int main() {
int a = -2; //原码:10000000 00000000 00000000 00000010 反码:11111111 11111111 11111111 11111101 补码:11111111 11111111 11111111 11111110
int b = a>>1; //补码:11111111 11111111 11111111 11111111 反码:11111111 11111111 11111111 11111110 原码:10000000 00000000 00000000 00000001
printf("%d", b); //结果为-1
return 0;
}