一、补码,反码

正数的源码=反码=补码

负数的反码等于符号位不变,其余位按位取反

负数的补码等于反码+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;
}


逻辑位移和算术位移规则