本文主要是介绍C语言之位运算/原码/反码/补码、进制间的转换、常规应用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
所谓位运算,就是对一个比特(Bit)位进行操作。比特(Bit)是一个电子元器件,8个比特构成一个字节(Byte),它已经是粒度最小的可操作单元了。
****进制间的转换:
16进制转换成10进制:例如0xff: 15*16^1+15*16^0=255。
10进制转换成16进制:用十进制数整除以16,然后取余数,直到商为0则停止转换。余数可以是0~15中的某一个数,其中0~9不用改变,10~15则分别用A~F表示。最先得到的余数是最低位,最后得到的是最高位,由高到低排列得到16进制数(逆序)。(也可以先八10进制转换成二进制,再把二进制转换成16进制.)
二进制转成10进制:数字中所有位*本位的权重然后求和,注意如果二进制有小数部分,小数部分的权重指数是负的;例如10101.101=1*2^4+0*2^3+1*2^2+0*2^1+1*2^0+1*2^-1+0*2^-2+1*2-3=21.625,
10进制转换二进制:将10进制数字不断除以2直到商为零,然后将余数由下至上依次写出(逆序),即可得到该数字的二进制表示.
10进制小数转换成二进制:十进制小数转R进制小数(其他进制方法类似),方法为乘R取整,每次乘以相应之后基数后取结果的整数部分即可,直到小数部分为0,但是并非所有的十进制小数都能完全转化为R进制小数,这时就需要根据要求的精度值,类似四舍五入(比如二进制0舍去,1进位)。例如:
0.625*2=1.25 整数1
0.25*2=0.5 整数0
0.5*2=1.0 整数1
结果按顺序排列得到0.101;
二进制转换成八进制:从右向左,每三位一组(不足三位的在左侧补0),转换成8进制;
8进制转换成二进制:用3位二进制代码替换每一位8进制;
二进制转行成16进制:从右向左,每四位一组(不足四位的在左侧补0),转换成16进制;
16进制转换成二进制:用四位二进制代码替换每一位16进制;
负数的16进制的表示方法:首先应该将其表示成二进制形式,然后变反码,再变补码,然后把补码每四位转换成16进制。
以-1来说:将-1表示成4位二进制数(求补码),就是1111(此时将1111当成有符号数),然后直接化16进制数;-1的一位有符号16进制数就是 F.
-10的8位有符号二进制数为11110110(补码表示),化为2位有符号的16进制数FA.
如何判断一个16进制数是正是负?
看有没有指明这个16进制数是否为有符号数,如果题目说明为无符号数,则表示正数. 如果为有符号数,就要判断符号的正负:将16进制数的最高位化为4位二进制数,如果所化的二进制数的最高位为1就表示负数,为0就表示正数.
例 FA 为有符号的16进制数,F为FA的最高位,化为二进制数为1111,而1111的最高位为1,就表示FA是个负数.FA化为10进制数就为 -10
负数的二进制表示:最左侧位0表示整数,1表示负数;
溢出:https://www.cnblogs.com/Jamesjiang/p/8947252.html
***常规使用:
为什么要byte要和0xff进行按位与运算?
当一个byte会转换成int时,由于int是32位,而byte只有8位这时会进行补位,例如补码11111111的十进制数为-1,转换为int时变为11111111 11111111 11111111 11111111,即0xffffffff但是这个数是不对的,这种补位就会造成误差。和0xff相与后,高24比特就会被清0了,结果就对了。
C语言提供了六种位运算符:
运算符 & | ^ ~ << >>
说明 按位与 按位或 按位异或 取反 左移 右移
*****************按位与运算(&)
一个比特(Bit)位只有 0 和 1 两个取值,只有参与&运算的两个位都为 1 时,结果才为 1,否则为 0。例如1&1为 1,0&0为 0,1&0也为 0,这和逻辑运算符&&非常类似。
C语言中不能直接使用二进制,&两边的操作数可以是十进制、八进制、十六进制,它们在内存中最终都是以二进制形式存储,&就是对这些内存中的二进制位进行运算。其他的位运算符也是相同的道理。
例如,9 & 5可以转换成如下的运算:
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在内存中的存储)
& 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在内存中的存储)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001 (1 在内存中的存储)
也就是说,按位与运算会对参与运算的两个数的所有二进制位进行&运算,9 & 5的结果为 1。
又如,-9 & 5可以转换成如下的运算:
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在内存中的存储)
& 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在内存中的存储)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在内存中的存储)
-9 & 5的结果是 5。
&是根据内存中的二进制位进行运算的,而不是数据的二进制形式;其他位运算符也一样。以-9&5为例,-9 的在内存中的存储和 -9 的二进制形式截然不同:
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在内存中的存储)
-0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (-9 的二进制形式,前面多余的 0 可以抹掉)
按位与运算通常用来对某些位清 0,或者保留某些位。例如要把 n 的高 16 位清 0 ,保留低 16 位,可以进行n & 0XFFFF运算(0XFFFF 在内存中的存储形式为 0000 0000 -- 0000 0000 -- 1111 1111 -- 1111 1111)。
【实例】对上面的分析进行检验。
#include <stdio.h>
int main(){
int n = 0X8FA6002D;
printf("%d, %d, %X\n", 9 & 5, -9 & 5, n & 0XFFFF);
return 0;
}
运行结果:
1, 5, 2D
**********按位或运算(|)
参与|运算的两个二进制位有一个为 1 时,结果就为 1,两个都为 0 时结果才为 0。例如1|1为1,0|0为0,1|0为1,这和逻辑运算中的||非常类似。
例如,9 | 5可以转换成如下的运算:
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在内存中的存储)
| 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在内存中的存储)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1101 (13 在内存中的存储)
9 | 5的结果为 13。
又如,-9 | 5可以转换成如下的运算:
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在内存中的存储)
| 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在内存中的存储)
-----------------------------------------------------------------------------------
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在内存中的存储)
-9 | 5的结果是 -9。
按位或运算可以用来将某些位 置 为1,或者保留某些位。例如要把 n 的高 16 位置 1,保留低 16 位,可以进行n | 0XFFFF0000运算(0XFFFF0000 在内存中的存储形式为 1111 1111 -- 1111 1111 -- 0000 0000 -- 0000 0000)。
【实例】对上面的分析进行校验。
#include <stdio.h>
int main(){
int n = 0X2D;
printf("%d, %d, %X\n", 9 | 5, -9 | 5, n | 0XFFFF0000);
return 0;
}
运行结果:
13, -9, FFFF002D
*********按位异或运算(^)
参与^运算两个二进制位不同时,结果为 1,相同时结果为 0。例如0^1为1,0^0为0,1^1为0。
例如,9 ^ 5可以转换成如下的运算:
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在内存中的存储)
^ 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在内存中的存储)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1100 (12 在内存中的存储)
9 ^ 5的结果为 12。
又如,-9 ^ 5可以转换成如下的运算:
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在内存中的存储)
^ 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在内存中的存储)
-----------------------------------------------------------------------------------
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0010 (-14 在内存中的存储)
-9 ^ 5的结果是 -14。
按位异或运算可以用来将某些二进制位反转。例如要把 n 的高 16 位反转,保留低 16 位,可以进行n ^ 0XFFFF0000运算(0XFFFF0000 在内存中的存储形式为 1111 1111 -- 1111 1111 -- 0000 0000 -- 0000 0000)。
【实例】对上面的分析进行校验。
#include <stdio.h>
int main(){
unsigned n = 0X0A07002D;
printf("%d, %d, %X\n", 9 ^ 5, -9 ^ 5, n ^ 0XFFFF0000);
return 0;
}
运行结果:
12, -14, F5F8002D
**************取反运算(~)
取反运算符~为单目运算符,右结合性,作用是对参与运算的二进制位取反。例如~1为0,~0为1,这和逻辑运算中的!非常类似。。
例如,~9可以转换为如下的运算:
~ 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在内存中的存储)
-----------------------------------------------------------------------------------
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0110 (-10 在内存中的存储)
所以~9的结果为 -10。
例如,~-9可以转换为如下的运算:
~ 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在内存中的存储)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1000 (9 在内存中的存储)
所以~-9的结果为 8。
【实例】对上面的分析进行校验。
#include <stdio.h>
int main(){
printf("%d, %d\n", ~9, ~-9 );
return 0;
}
运行结果:
-10, 8
************左移运算(<<)
左移运算符<<用来把操作数的各个二进制位全部左移若干位,高位丢弃,低位补0。
例如,9<<3可以转换为如下的运算:
<< 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在内存中的存储)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0100 1000 (72 在内存中的存储)
所以9<<3的结果为 72。
又如,(-9)<<3可以转换为如下的运算:
<< 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在内存中的存储)
-----------------------------------------------------------------------------------
1111 1111 -- 1111 1111 -- 1111 1111 -- 1011 1000 (-72 在内存中的存储)
所以(-9)<<3的结果为 -72
如果数据较小,被丢弃的高位不包含 1,那么左移 n 位相当于乘以 2 的 n 次方。
【实例】对上面的结果进行校验。
#include <stdio.h>
int main(){
printf("%d, %d\n", 9<<3, (-9)<<3 );
return 0;
}
运行结果:
72, -72
************右移运算(>>)
右移运算符>>用来把操作数的各个二进制位全部右移若干位,低位丢弃,高位补 0 或 1。如果数据的最高位是 0,那么就补 0;如果最高位是 1,那么就补 1。
例如,9>>3可以转换为如下的运算:
>> 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在内存中的存储)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001 (1 在内存中的存储)
所以9>>3的结果为 1。
又如,(-9)>>3可以转换为如下的运算:
>> 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在内存中的存储)
-----------------------------------------------------------------------------------
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 1110 (-2 在内存中的存储)
所以(-9)>>3的结果为 -2
如果被丢弃的低位不包含 1,那么右移 n 位相当于除以 2 的 n 次方(但被移除的位中经常会包含 1)。
【实例】对上面的结果进行校验。
#include <stdio.h>
int main(){
printf("%d, %d\n", 9>>3, (-9)>>3 );
return 0;
}
运行结果:
1, -2
=============原码、反码、补码:
一、什么是原码、反码和补码
在计算机内部存储的带符号数都是以补码形式存储,用补码形式进行运算的。以整型数为例,且假定字长为8位。
1、原码
整数X的原码是指:其符号位为0表示正,为1表示负;其数值部分就是X的绝对值的二进制数。X的原码通常用【X】原表示。如:
【+100】原=01100100 【+0】原=00000000
【-100】原=11100100 【-0】原=10000000注意:在原码中,零有两种表示形式。
原码表示法简单易懂,与真值(带符号数本身)转换方便,只要符号还原即可,但当两个正数相减或不同符号数相加时,必须比较两个数哪个绝对值大,才能决定谁减谁,才能确定结果是正还是负,所以原码不便于加减运算。
2、反码
X的反码是指:对于正数,反码与原码相同;对于负数,符号位不变,其数值位X的绝对值取反(1变0,0变1)。X的反码通常用【X】反来表示。如
【+100】反=01100100 【+0】反=00000000
【-100】反=10011011【-0】反=11111111
注意:在反码中,零也有两种表示形式。
反码运算也不方便,通常用来作为求补码的中间过渡。
3、补码
X的补码是指:对于正数,补码与原码相同;对于负数,符号位不变,其数值位X的绝对值取反后在最低位加1。X的补码通常用【X】补来表示,实际上,【X】补=【X】反+1。如:
【+100】补=01100100 【+0】补=00000000
【-100】补=10011100 【-0】补=00000000
注意:在补码中,零有唯一的编码,【+0】补=【-0】补=00000000。
补码运算简单方便,符号位可以作为数据的一位参与运算,不必单独处理;二进制的减法可用其补码的加法来实现,简化了硬件电路。
1. 负数原码和反码的相互转化负数原码转化为反码:符号位不变,数值位按位取反。如:原码 1100 0010
反码 1011 1101负数反码转化为原码:符号位不变,数值位按位取反。反码 1011 1101
原码 1100 00102. 负数原码和补码的相互转化负数原码转化为补码:符号位不变,数值位按位取反,末尾加一。原码 1100 0010
反码 1011 1101 //符号位不变,数值位按位取反
补码 1011 1110 //末尾加1负数补码转化为原码:符号位不变,数值位按位取反,末尾加1。补码 1011 11101100 0001 //符号位不变,数值位按位取反
原码 1100 0010 //末尾加13.负数反码和补码的相互转化负数反码转化为补码:末尾加1。反码 1011 1101
补码 1011 1110负数补码转化为反码:末尾减1(注意,此处的反码是指原码的反码)。补码 1011 1110
原码的反码 1011 1101
//减法 借位
补码转换为原码:符号位不变,数值位按位取反,末位再加1。即补码的补码等于原码.
一个汉子等于两个字节,包括中文标点符号;一个英文占一个字节;
两个16进制数等于一个字节;一个字节占8位,最多表示-128~127或者0~255;
很多计算,都使用0x这样的16进制进行运行:如 0xF的二进制为 1111 ,即四个1。0xFF的二进制为 1111 1111 ,即8个1的二进制形式每多一个F就是多一个4位的1111。最多8个F。
***16进制的反码和补码:
将一个十六进制整数按位取反并加 1,就生成了它的补码。一个简单的十六进制数字取反方法就是用 15 减去该数字。下面是一些十六进制数求补码的例子:
6A3D --> 95C2 + 1 --> 95C3
95C3 --> 6A3C + 1 --> 6A3D
通过检查十六进制数的最高有效(最高)位,就可以知道该数是正数还是负数。如果最高位 ≥ 8,该数是负数;如果最高位 ≤ 7,该数是正数。比如,十六进制数 8A20 是负数,而 7FD9 是正数.
这篇关于C语言之位运算/原码/反码/补码、进制间的转换、常规应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!