1.原码,反码,补码
int a=1;
整形占用四个字节----32bit
00000000 00000000 00000000 00000001 (数值位)
1.1原码,反码,补码的介绍
- 整数的2进制表示方法有三种,即原码,反码,补码
- 三种表⽰⽅法均有符号位和数值位两部分,符号位都是⽤0表⽰“正”,⽤1表⽰“负”,⽽数值位最⾼位的⼀位是被当做符号位,剩余的都是数值位。
- 正整数的原,反,补码都相同
- 负整数的三种表示方法各不相同
- 原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
- 反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
- 补码:反码+1就得到补码。
- 原码和补码的相互转换均为:先取反,后+1.
1.2补码的作用
- 对于整形来说:数据存放内存中其实存放的是补码。
为什么?
1. 在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀ 处理;
2. 同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路。例如1-1可以转化为1+(-1)
#include <stdio.h>
//int main()
//{
// int a = 5;
// //00000000000000000000000000000101 - 原码
// //00000000000000000000000000000101 - 反码
// //00000000000000000000000000000101 - 补码
//
// int b = -5;
// //10000000000000000000000000000101 - 原码
// //11111111111111111111111111111010 - 反码
// //1111 1111 1111 1111 1111 1111 1111 1011 - 补码
// //f f f f f f f b
// //0x ff ff ff fb --->内存中的形式
// return 0;
//}
另外在计算时补码符合计数规律
int main()
//{
// //1-1
// //1+(-1) = 0
// //00000000000000000000000000000001
// //10000000000000000000000000000001
// //10000000000000000000000000000010--> -2 原码相加之和
// //
// //00000000000000000000000000000001 1的补码
// //11111111111111111111111111111111 -1的补码
// //00000000000000000000000000000000 加1之后符号位的1到了第33位,消掉。补码相加之和
//由此可见补码相加才是正确的答案
// //11111111111111111111111111111111
// //10000000000000000000000000000000
// //10000000000000000000000000000001
// return 0;
//}
2.位移操作符
- <<左移操作符
- >>右移操作符
- 注:移位操作符的操作数只能是整数。
2.1左移操作符
- 移位规则:左边抛弃、右边补0
例如:
//int main()
//{
// int a = -10;
// int b = a << 1;
// //10000000000000000000000000001010
// //11111111111111111111111111110101
// //11111111111111111111111111110110
// //a<<1
// //11111111111111111111111111101100
// //10000000000000000000000000010011
// //10000000000000000000000000010100
// //-20
// printf("b = %d\n", b);
//
// return 0;
//}
2.2右移操作符
位移规则:首先右移运算分两种
1.逻辑右移:左边⽤0填充,右边丢弃
2.算术右移:左边⽤原该值的符号位填充,右边丢弃
举例如下:
//int main()
//{
// int num = -1;
// //10000000000000000000000000000001
// //11111111111111111111111111111110
// //11111111111111111111111111111111
// //
// int n = num >> 1;
// printf("%d\n", n);
//
// return 0;
//}结果仍为-1
警告⚠⚠:对于移位运算符,不要移动负数位,这个是标准未定义的。
2.3逻辑右移与算术右移的区别
- 逻辑右移就是不考虑符号位,右移一位,左边补零即可。 算术右移需要考虑符号位,右移一位,若符号位为1,就在左边补1,;否则,就补0。
- 算术右移也可以进行有符号位的除法,右移n位就等于除2的n次方。 例如,8位二进制数11001101分别右移一位。
3.位操作符:&(与),|(或),^(异或),~(取反)
3.1&(与):按位与 -- 对应的二进制位,有0则为0,两个同时为1才是1
int main()
{
int a=5;
int b=-6;
int c=a&b;
//00000000000000000000000000000101 5的补码
//11111111111111111111111111111010 6的补码
//00000000000000000000000000000000 结果
printf("%d",c);
return 0;
}//最终结果为0
3.2|(或):按位或 -- 对应的二进制位上,有1则为1,两个同时为0才是0
int main()
{
int a=5;
int b=-6;
int c=a&b;
//00000000000000000000000000000101 5的补码
//11111111111111111111111111111010 6的补码
//11111111111111111111111111111111 结果
printf("%d",c);
return 0;
}//最终结果为-1
3.3^(异或):按位异或 -- 对应的二进制位上,相同为0(真),相异则为1(假)
int main()
{
int a=5;
int b=-6;
int c=a&b;
//00000000000000000000000000000101 5的补码
//11111111111111111111111111111010 6的补码
//11111111111111111111111111111111 结果
printf("%d",c);
return 0;
}//最终结果为-1
3.4~(取反):按位取反 -- 二进制位,0变1,1变0
int main()
{
int n = 0;
int a = ~n;
//00000000000000000000000000000000
//11111111111111111111111111111111
//包括符号位全部取反
printf("%d",a);
return 0;
}因此a=-
3.5那这为操作符有什么具体作用呢?
- 举例1(&)
//想知道数字21的二进制序列中的最低位是几
int main()
{
int a=21;
//a&1
//00000000000000000000000000010101 21的补码
//00000000000000000000000000000001
//00000000000000000000000000000001 结果为1
return 0;
}
//因此我们可以通过按位与的这种方式来确定具体的某一位是几
//例如如果我们想知道第5位是几时,只需要向右移动4位
//(a>>4)&1
即可
- 举例2(^)(难点)
题:不创建临时变量(第三个变量),实现两个数的交换
int main()
{
int a=3;
int b=5;
a=a+b;
b=a-b;
a=a-b;
printf("%d %d",a,b);
return 0;
}
//可是存在一个问题:范围溢出。即当a和b过大时会出现错误。
解决方法:
int main()
{
int a=3;
int b=5;
a=a^b;
b=a^b;
a=a^b;
printf("%d %d",a,b);
return 0;
}//发现也能得出结果
- 举例3(或,取反)
int main()
{
//将a的二进制中的di5位改成1
int a=13;
//00000000000000000000000000001101
//1<<4
//00000000000000000000000000010000
a = a | (1<<4);
printf("a=%d",a);//a=29
//将a的二进制中第5位改成0
//00000000000000000000000000001101
//11111111111111111111111111101111 ~(1<<4)
a = a &~(1<<4);
printf("a=%d",a);//a=13
return 0;
}
结论:
- 0^a=a a^a=0 3 ^ 3 ^ 5=5,3 ^ 5 ^ 3=5.所以得出异或支持交换律
- 这种异或操作是有局限性的:
- 只能作用于整数交换
- 代码可读性差
- 代码的执行效率也是低于设置3个参数的
3.6练习:编写代码实现:求一个整数储存在内存中的二进制中1的个数。
int main()
{
int a = 15;
//1 1 1 1
//8 4 2 1
//15%2 = 1
//15/7 = 7
//1 1 1
//7%2 = 1
int count=0;
while(a)
{
if(a%2 == =)
{
count++;
}
a = a/2;
}
printf("count=%d",count);
return 0;
}但是当你输入负数的时候,就会出现错误
//法二
int main()
{
int a = 0;
scanf("%d", &a);
int i = 0;
int count = 0;
for (i = 0; i < 32; i++)
{
if (((a >> i) & 1) == 1)
{
count++;
}
}
printf("%d\n", count);
return 0;
}
//法三,mage进化
int main()
{
int n = 0;
scanf("%d", &n);
int count = 0;
while (n)
{
n = n & (n - 1);//每进行一次循环都会减去一个一
count++;
}
printf("%d\n", count);
return 0;
}
4.逗号表达式
exp1, exp2, exp3, …expN
- 逗号表达式,就是⽤逗号隔开的多个表达式。
- 逗号表达式,从左向右依次执⾏。整个表达式的结果是最后⼀个表达式的结果。
举例:
//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c为13
//代码2
if (a =b + 1, c=a / 2, d > 0)
//代码3
a = get_val();
count_val(a);
while (a > 0)
{
//业务处理
a = get_val();
count_val(a);
}
如果使⽤逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{
//业务处理
}