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 0010

 2.  负数原码和补码的相互转化
  负数原码转化为补码:符号位不变,数值位按位取反,末尾加一。

原码 1100 0010
反码 1011 1101 //符号位不变,数值位按位取反
补码 1011 1110 //末尾加1
  负数补码转化为原码:符号位不变,数值位按位取反,末尾加1。

补码 1011 1110
       1100 0001 //符号位不变,数值位按位取反
原码 1100 0010 //末尾加1

 3.负数反码和补码的相互转化
  负数反码转化为补码:末尾加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 是正数.

 

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值