位操作符和二进制有一定的关系,我们先铺垫一些二进制和进制转换的知识。
1.二进制和进制转换
在计算机语言中,我们常用的进制有二进制,八进制,十进制,十六进制。那是什么意思呢?其实这些进制的不同仅仅只是数值的不同表示形式而已。
1.1二进制
在介绍二进制之前,我们先从十进制讲起。
1.十进制中满10进1
2.十进制中的数字是由0-9组成的
其实二进制也是一样的
1.二进制中满2进1
2.二进制中的每一位数字都是由0-1组成的
1.2二进制转十进制
十进制中的123表示的值是一百二十三,为什么是这个值呢?是因为它的每一位都是由权重的。十进制中的数字从右到左是个位,十位,百位,分别的权重便是10的0次方,10的1次方,10的2次方,以此类推即可。
二进制和十进制是类似的,只不过二进制中每一位的权重不同,自右向左为2^0,2^1,2^2......
如果是二进制的1101,应该怎么理解呢?如图所示!
下面讲解一下10进制转2进制度的方法,在这里我们可以使用短除法,求出每次除2的余数。从下往上的余数就是十进制转出的二进制,由图所示。125转换的二进制数则是1111101
1.3二进制转8进制
8进制的数字每一位是0-7的数字,而0-7的数字写成二进制,最多需要三个二进制位就够了(2^3=8)。比如7的二进制为111,所以在二进制转八进制的时候,从二进制右边的序列中从低位开始,向左边每三位换算一个八进制位,剩余不够三个二进制位的直接换算即可。
例如二进制的01101011换成八进制为153。
但由于计算机中常用的进制较多,为了区分使用的进制,我们在八进制的数字开头添加一个0,所以01101011换算为八进制应为0153.
1.4二进制转16进制
16进制的数字每⼀位是0~9,a~f的,0~9,a~f的数字,各⾃写成2进制,最多有4个2进制位就⾜够了,
⽐如f的⼆进制是1111,所以在2进制转16进制数的时候,从2进制序列中右边低位开始向左每4个2进
制位会换算⼀个16进制位,剩余不够4个⼆进制位的直接换算。
如:2进制的01101011,换成16进制:0x6b,16进制表⽰的时候前⾯加0x
2.原码、补码、反码
整数的二进制表示方法由三种,即原码、补码、反码。
有符号整数的三种表示方法均是由符号位和数值位两部分构成的,在二进制序列中,最高位的1位被当作符号位,剩余的表示数值位。
符号位都是⽤0表⽰“正”,⽤1表⽰“负”。
正整数的原码,反码,补码都是相同的。
负整数的三种表示方法各不相同。
原码:直接将数值按照正负数
#include <stdio.h>
int main()
{
int a = 13;
a = a | (1 << 4);
a = a & ~(1 << 4);
printf("a = %d\n", a);
return 0;
}
的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。
对于整型来说:数据是以补码的形式存放在内存当中的。
为什么呢?
这是因为在计算机系统中,使用补码可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理,此外,补码和原码之间的互相转化的运算过程是相同的,不需要额外的硬件电路。
3.移位操作符
移位操作符分为两种,一种是左移操作符<<,他的作用是将二进制数左移一位,一种是右移操作符>>,他的作用是将二进制数右移一位。下面具体的介绍移位的规则。
3.1左移操作符<<
移位规则:左边抛弃一位,右边补一位0。
性质:左移一位相当于*2
#include <stdio.h>
int main()
{
int num = 10;
int n = num << 1;
printf("n= %d\n", n);
printf("num= %d\n", num);
return 0;
}
在这段代码中,我们将10左移一位。10为00001010,我们将其左边抛弃一位,右边补一位0.则变为了00010100.
3.2右移操作符
首先右移运算分为两类,分别是逻辑右移和算数右移,编译器一般采用的是算术右移。
逻辑右移:左边用0填充,右边抛弃。
算数右移:左边用原数的符号位填充,右边丢弃。
#include <stdio.h>
int main()
{
int num = 10;
int n = num >> 1;
printf("n=%d\n", n);
printf("num=%d", num);
return 0;
}
4. 位操作符
位操作符有四个
1.& 按位与-->两个数按位与,补码中相对应的位都是1则为1,否则为0.
2.| 按位或-->两个数按位或,补码中相对应的位有1则为1,否则为0。
3.^ 按位异或-->两个数按位异或,补码中相对应的位相同则为0,否则为1.
4.~ 按位取反-->对一个数按位取反,将原二进制位的0变1,1变0.
我们通过代码来理解这四个位操作符
#include <stdio.h>
int main()
{
int num2 = -5; //10000101 原码
//11111010 反码
//11111011 补码
int num1 = 3; //00000011 正数的原码反码补码相同
//& 00000011
// | 11111011 放的是补码,我们要对补码取反10000100,
// 然后+1,所以最终结果是这个-->1000101。也就是-5。
// ^ 11111100 取反 10000011 10000100 也就是-8
printf("%d\n", num1 & num2);
printf("%d\n", num1 | num2);
printf("%d\n", num1 ^ num2);
printf("%d\n",~0);//11111111 取反10000000 +1:100000001 也就是-1
return 0;
}
5.操作符试题训练
T1.不能创建临时变量,实现两个数的交换。
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a ^ b;
b = a ^ b;//(a^b)^b==a 此时b保存的是a的量
a = a ^ b;//(a^b)^a==b 此时a保存的是b的量
return 0;
}
T2.通过编写代码,求一个整数存储在内存中的二进制中1的个数。
方法1.
#include <stdio.h>
int main()
{
int num = 10;
int count = 0;
while (num)
{
if (num % 2 == 1)
count++;
num /= 2;
}
printf("⼆进制中1的个数 = %d\n", count);
return 0;
}
方法2.
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int i = 0;
int count = 0;
for (int i = 0; i < 32; i++)
{ //假如num为1
if (num & (1 << i)) //11111111
{ // & 00000001
count++; // 00000001 不为0,则为真,则进入。
}
}
return 0;
}
方法3.
#include <stdio.h>
int main()
{
int n = 0;
int count = 0;
scanf("%d", &n);
while (n)
{ // n n-1 n&(n-1)
n = n & (n - 1);// 1111 1110 1110
count++; // 这种方法可以让最后一个位变为0
}
return 0;
}
T3.二进制位置0或者置1
编写代码将13⼆进制序列的第5位修改为1,然后再改回0
#include <stdio.h>
int main()
{
int a = 13;
a = a | (1 << 4);
a = a & ~(1 << 4);
printf("a = %d\n", a);
return 0;
}