前提所备知识:原码,反码,补码以及二进制
一:在计算机中存的是二进制数,其中二进制数包括无符号整数和有符号整数,其中无符号整数的范围为有符号整数的两倍,例如8个比特位中,有符号整数对应-127到127,无符号整数对应0到255
二:一个整数大小为4个字节,也就是32个比特位,每1个比特位就对应一个二进制数,也就是说整数1在计算机中二进制表达为00000000000000000000000000000001(总共32位数)。其中第一位数代表符号,0代表正数,1代表负数。比如100000……0000001它的第一位是1,所以这个数是负数,0111010……000001它的第一位是0,所以这个数是正数。
三:正数存储在计算机中,原码,反码和补码都是一样的,比如整数13在计算机中,它表示为00000000000000000000000000001101,它的原码,反码,补码都是这个数。
但是负数存储在计算机中,存储的是它的补码。
四:负数的原码,反码,补码转换方式如下:
更推荐记法二,因为变换公式一样记得更方便(原码变补码为取反加1,补码变原码也是取反加1)
五:计算机只能运算加法,不能运算减法,(因为计算机只有加法器),所以想要进行减法,其实也就是加上负数罢了
简单复习下前提所备的知识后就可以学习新的知识了
第一:左移操作符<<(箭头向左的是左移)
移动规则:左边抛弃,右边补零
第二:右移操作符>>(箭头向右的是右移)
移动规则:1.逻辑右移——右边抛弃,左边补零
2.算术右移——右边抛弃,左边补符号位的数(如果是正数就补0,负数就补1)
其中根据编译器的不同,而选择逻辑右移或算术右移(但绝大多数的编译器都是算术右移)
注:不要移动负数位,这个是标准未定义的
例如:
第三:按位与 &
类比:&&是并且,也就是说两真同时为真才为真,&是按位与,也就是说两数都为1则为1,否则为0。例如:一个数是13&5也就是1101&0101,比较对应位的数,得到0101,也就是5,所以13&5==5
第四:按位或 |
类比:||是或者,也就是说两个有一真则为真,|是按位与,也就是说两数有1就为1,没1就为0(因为计算机中1代表真)。例如:一个数是13|5也就是1101|0101,比较对应位的数,得到1101,也就是13,所以13|5==13
第五:按位异或 ^(在数字6上面)
不相同才为1,相同为0。例如:13^5也就是1101^0101,比较对应位的数,得到1000,也就是8,所以13^5==8
公式一:a^a==0
理解:因为两数的二进制数相同,所以对应的每一位的数都相同,相同为0,所以所有都数按位异或后都为0,对应的十进制数也就是0
公式二: 0^a==a
理解:因为0的二进制数是32个0,所以a中有1的位数按位异或0还是1(不相同为1),a中有0的位数按位异或还是0(相同为0)
第六:取反 ~
也就是0变为1,1变为0
注:以上六个操作符的操作数必须是整数
练习1:
一道面试题目:不能创建临时变量(第三个变量),实现两个数的交换。
很容易想到的采用临时变量方法:
当题目做了要求后,那么就需要另寻他法了
另一种方法:
这样也能完成换算,但是会有一个问题,那就是当a和b的数很大的时候,a+b时就有可能溢出整数类型的最大值,所以是有缺陷的
采用位操作符解决的方法:
这样既没有使用临时变量,也没有溢出的缺陷,唯一缺点就是想不到(所以只能多刷题,多看看别人写的代码,积累方法了)
练习2:
编写代码实现:求⼀个整数存储在内存中的⼆进制中1的个数。
类比:要得到1234的每一位的数,一般采用的方法是1234%10=4,再1234/10=123,然后123%10=3,再123/10=12……当到最后一位1时,1/10=0,代表已经是个位数了,所以停止循环。因为1234是十进制的数,所以是%10和/10,因此对于二进制数就是%2和/2
方法一:
但是这样只适用于正数,负数就不行了(例如-1对应的二进制是32个1,但是-1/2==0,不符合实际),所以我们需要稍微修改一下,让int类型转变为unsigned(无符号)类型,这样即使输入-1,也会让-1所对应的有符号二进制数转变为无符号二进制数(即32个1的正数)
方法二:
思路:用二进制的1一位一位对应上去,直到左移32位变为32个0时,停止,如果对应的数是1,计数器加1
但是还能更优化,因为这个办法必须循环32次
答案:
方法三:
算法:n=n&(n-1)
举例:
由例可知,每&一次,说明含有1个1
这个算法就是用来除掉1的