(二进制)操作符详解

简介: (二进制)操作符详解

二进制与进制转换

2进制转10进制

无论对应什么进制而言,数值都是每一位的值乘以对应权重之和



10进制转2进制

方法1:不断模2除2取模和整除),直到为0,最后在把余数由下往上组合即为二进制


方法2:当你对2进制熟悉后,可以根据二进制的值进行大致的估算


比如,上图中,求21的二进制,则先估算1111对应15,所以加一位,从10000开始 ,然后看下一位权重为8,加上就超过21了,就再看下一位为4……依此类推,最终算出10101

2进制转8进制和16进制

8进制

从2进制序列中右边低位开始向左每3个2进制位会换算⼀个8进制位,剩余不够3个2进制位的直接换算


16进制

从2进制序列中右边低位开始向左每4个2进制位会换算⼀个16进制位,剩余不够4个2 进制位的直接换算


原码,反码,补码

整数的2进制表示方法有三种,即 原码、反码和补码

有符号 整数的三种表示方法均有符号位和数值位两部分,2进制序列中, 最高位 的1位是被当做 符号位 ,剩余的都是数值位。

符号位都是用 0表示“正” ,用 1表示“负” 。


 


这里有两种方法把补码变回原码第一种是减1,除符号位按位取反,第二种是直接除符号位按位取反,再加1

对于整形来说:数据存放内存中其实存放的是补码。

因为补码与原码相互转换,都可以除符号位按位取反,加1获得,所以运算过程相同


位移操作符


左移操作符

左移抛弃(包括符号位),右边补零;左移不改变m本身,只是会输出一个结果到n

上述例子中,-3的补码是11111111  11111111 11111111 11111101 ,那么我左移30位,最高位就为0,就变成超级大的正数


右移操作符

右移运算分两种(大部分编译器上是算术右移)

1. 逻辑右移:左边⽤0填充,右边丢弃

2. 算术右移:左边⽤原该值的符号位填充,右边丢弃


对于未溢出整数来说,左移一位,相当于乘以2,右移一位,相当于除以2


位操作符

&按位与,有0则为0,全1才为1

| 按位或,有1则为1,全0才为0

^按位异或,相同为0,相异为1


你可以动手算算下面三个位运算哦~


我们再来看看^按位异或的神奇性质,下列输出的a结果为多少?


相信你已经发现了奥秘,在异或的过程中其余数都有伴,而只有6是单身狗,嘻嘻~

所以,两个相同的数异或会消失,只有单独的会留下


我们再来看一道面试题:不能创建临时变量(第三个变量),实现两个数的交换


可能有很多小伙伴是这样实现的,乍一看好像没什么问题,实际上如果a和b是两个非常大的整数,它们之间的运算可能会造成溢出,导致结果出错


实际上,这里利用刚刚异或的性质就能轻松解决 ,这里a先存下a^b,等到与b异或时,里面的b相同就消失了,只剩下a,就可以赋给b;同样,实际上为a的b与实际上为a^b的a异或,a相同就消失了,留下b赋给a

这就好像一个破译密码的过程,a^b是加密过的密文,用a密码本就可以破译出b的信息,用b密码本就可以破译出a的信息


练习

练习1:求一个整数存储在内存中的二进制中1的个数

方法一

根据定义,模2除2,看看有多少1


这样看,好像可以,其实这段代码还有漏洞

当输入的数为负数时,就失效了 。那要怎么改呢?其实很简单,把输入类型改为无符号整型即可,这样编译器就会把负数看为一个特别大的正数了。


方法二

运用位移操作符与位操作符,让每一位与1按位与,1&1为1,0&1为0,这样就可以算出二进制位中有多少个1

方法三

这个方法也是最高效的,利用每运算一次n&(n-1),就会消去二进制中最右边的1的性质


我们可以发现,下图中n每进行一次这种运算,二进制位最右边的1就变成0,直到全为0

这种方式是不是很好?达到了优化的效果,但是难以想到。

这个方法还可以有很多迁移运用,比如这道题:判断一个数n是否是2的次方数


练习2: 二进制位置0或者置1


编写代码将13⼆进制序列的第5位修改为1,然后再改回0


置为1

置为1:将1左移4位,再与13按位或,1和0为1,0和0为0,只把第5位改成1


置为0

置为0:将1左移4位,再按位取反,得到11111111 11111111 11111111 11101111,再去和13按位与,1和0为0,1和1为1,只把第5位改成0


 

练习3:求两个数二进制中不同位的个数


这题其实就是前面练习1的变式,分为两步。第一步,先把两个数异或,根据异或的特点,相同为0,相异为1,这样只要统计1的个数,就是二进制位中不同位的个数。第二步,利用n&(n-1)高效消除1的特点,来统计1的个数


代码如下:


int main()
{
    int a, b;
    while (scanf("%d %d", &a, &b) != EOF)
    {
        int c = a ^ b;
        int count = 0;
        while (c)
        {
            c = c & (c-1);
            count++;
        }
        printf("%d\n",count);
    }
    return 0;
}
相关文章
|
2天前
|
存储 编译器 C语言
【表达式求值】整型提升和算术转换
【表达式求值】整型提升和算术转换
20 0
|
2天前
|
存储 C语言 C++
截断&&整型提升&&算数转换
截断&&整型提升&&算数转换
|
7月前
|
C语言
C语言之将十进制整数转换为任意进制整数
C语言之将十进制整数转换为任意进制整数
158 0
|
8月前
|
C语言
C语言:截断+整型提升+算数转换练习
截断+整型提升+算数转换练习
42 0
|
2天前
|
存储 C语言
C中负数的存储形式 | 位运算符
C中负数的存储形式 | 位运算符
13 0
|
2天前
|
编译器 C语言 C++
整形提升和算数转换
整形提升和算数转换
20 0
|
2天前
|
存储 Java 程序员
基本概念【变量和数据类型和运算符、二进制和十进制、十进制转二进制 、二进制转十进制 】(一)-全面详解(学习总结---从入门到深化)
基本概念【变量和数据类型和运算符、二进制和十进制、十进制转二进制 、二进制转十进制 】(一)-全面详解(学习总结---从入门到深化)
35 0
|
6月前
|
存储 编译器 C语言
【C语言】整数的二进制以及移位操作符
【C语言】整数的二进制以及移位操作符
67 0
|
6月前
|
Linux C语言 C++
操作符&算数转换题
操作符&算数转换题
35 0
|
6月前
|
C语言
C语言:二进制、八进制、十六进制整数的书写及输出
C语言:二进制、八进制、十六进制整数的书写及输出