Hi~!这里是奋斗的小羊,很荣幸各位能阅读我的文章,诚请评论指点,关注+收藏,欢迎欢迎~~
💥个人主页:小羊在奋斗
💥所属专栏:C语言
本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为同样是初学者的学友展示一些我的学习过程及心得。文笔、排版拙劣,望见谅。
1、操作符的分类
2、原码、反码、补码
3、移位操作符
4、位操作符
5、逗号操作符
1、操作符的分类
(1)算术操作符:+、-、*、/、%
(2)移位操作符:<<、>>
(3)位操作符:&、|、^、~
(4)赋值操作符:=、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=
(5)单目操作符:!、++、--、&、*、+、-、~、sizeof、(类型)
(6)关系操作符:>、>=、<、<=、==、!=
(7)逻辑操作符:&&、||
(8)条件操作符:?:
(9)逗号表达式:,
(10)下标引用:[ ]
(11)函数调用:()
(12)结构体成员访问:. 、->
2、原码、反码、补码
整数的二进制表示方法有三种,即原码、反码和补码,有符号整数的三种表示方法均由符号位和数值位组成,二进制序列中,最高位的1位是符号位,后面的都是数值位。符号位0表示正,1表示负。
整数的原码、反码和补码都相同,负数的三种表示方法各有不同。
原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码;
反码:将原码的符号位不变,数值位按位取反就是反码;
补码:反码+1得到补码。
原码转换补码、补码转换原码都是取反+1
对整型来说,数据在内存中存放的是补码。为什么呢?
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码可以将符号位和数值位统一处理。同时,加法和减法也可以统一处理,因为CPU只有加法器。此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
比如,我们计算1-1,因为CPU只有加法器,所以我们用1+(-1)的形式计算:
3、移位操作符
(1)移动的是存储在内存中二进制位(补码);
(2)移位操作符的操作数只能是整数;
3.1 左移操作符:<<
移位规则:左边抛弃,右边补0
将10左移一位:
将-5左移一位:
规律:左移一位有乘2的效果;同样的,左移n位等于乘以2的n次方。
3.2 右移操作符:>>
移位规则:(1)逻辑右移:左边补0;右边丢弃
(2)算术右移:左边补原该值的符号位,右边丢弃
逻辑右移或算术右移是取决于编译器的,通常采用的都是算术右移。
将10右移一位:
将-4右移一位:
规律:右移一位有除2的效果,同样的,右移n位等于除以2的n次方。
注意:对于移位操作符,不要移动负数位,这个是未定义的。
4、位操作符
注意:它们的操作数也必须都是整数。
&:按位与(有0则为0)
因为3与-6的结果为正数,补码和原码相同,所以这里直接用了补码计算。
|:按位或(有1则为1)
^:按位异或(相同为0,相异为1)
~:按位取反(单目操作符)
例题1: 不能创建临时变量,实现两个整数的交换
方法一:要实现两个整数的交换,我们首先会想到创建一个临时变量来解决。
这无疑是一个最简单高效的方法。但题目明确说明了不能创建临时变量,那我们就要另想办法了。
方法二:既然不能创建临时变量,那我们只能对这两个数本身下手了。
大家觉得上面这个办法怎么样?我们按照题目要求完成了任务。
但是,这个办法是受限的。如果两个整数太大的话相加会溢出,那有没有完美的办法呢?
既然这样问,那答案肯定是有的,办法就在我们上面新学到的知识中。
方法三:使用异或操作符
不知道你第一次看到这个代码的时候有没有懵逼呢?反正我是挺惊讶的。那接下来我们就来分析上面的代码具体是怎么实现的。
首先我们知道,按位异或操作符的规则是:相同为0,想异为1。因为4^4=0,所以a^a=0;因为4^0=4,所以a^0=a;因为4^4^5=5,经过计算4^5^4=5,所以异或操作符是支持交换律的。
因为异或操作符不存在进位,所以不会发生溢出。 需要说明的是,这只是我们为了加深对异或操作符的理解而想出的一个题目,未来我们交换两个整数还是用创建临时变量的方法更好,可读性高,效率高。
例题2:编写代码求一个整数存储在内存中的二进制中1的个数
方法一:我们可以想办法拿到二进制的每一位,然后统计1的个数。
说起拿到二进制的每一位,就想到了我们之前的一个例题,其中有拿到十进制数的每一位的方法,通过模10除10即可;同样的,我们也可以通过模2除2来得到二进制数的每一位。
10的二进制表示为1010,有2个1。 看似我们完成了要求,但当我们输入负数的时候,结果却是错的。原因在于形参是有符号的整型,我们把形参定义为无符号的整型,就可以解决问题。
方法二:对整数二进制的最低位与1再向右移位,循环执行。这个方法不用关心是不是有符号无符号数的问题。
我们利用按位与的特点,如果二进制最低位是1,按位与1得到1;如果二进制最低位是0,按位与1得到0;再循环执行,我们就能得到二进制中1的个数。
这个方法也是可行的, 但它还不是最优的。
方法三:其实有一个专门用来计算一个数的二进制表示中有多少个1的算法:n &= (n - 1)。
这个算法牛逼的地方在于,n &= (n - 1) 这个式子执行一次,二进制表示的数就会少一个1,执行多少次,就有多少个1;也就是说输入的数有几个1就执行几次,效率很高。
这种算法除非见过,一般人还真想不出来,不过我们记住就行,不必太执着其中的原理。
例题3:判断一个数是不是2的次方数
2的次方数,有没有什么特点呢?通过上面我们了解了二进制,很容易就能想到,2的次方数二进制表示中只有一个1,那我们利用上面方法三中的代码判断结果是不是1就行了。虽然能解决问题,但是这个方法有点啰嗦。
我们知道2的次方数二进制表示中只有一个1,而 n &= (n - 1) 这个式子执行一次,二进制表示的数就会少一个1,那如果 n &= (n - 1) 等于0的话,不就说明 n 是2的次方数吗?
例题4:二进制位置0或置1
编写代码将11二进制表示的第五位修改为1,然后再改回0。
11的二进制表示:00000000000000000000000000001011
修改第五位为1: 00000000000000000000000000011011
再将第五位改回:00000000000000000000000000001011
只改第五位,其他位不能改变要怎么实现呢?这就要用到我们学过的按位或(|)操作符了,我们知道,按位或操作符的规则是有1则为1,那我们给11的二进制按位或00000000000000000000000000010000就可以实现只修改第五位而其他位不变的效果,而这个数我们只需要给1向左移4位就能得到。
那再将第五位修改回来,方法应该也跟上面差不多。这里就要用到按位与(&)操作符,我们知道按位与操作符的规则是有0则为0,那我们给修改过的数按位与上11111111111111111111111111101111就可以实现只修改第五位而保持其他位不变的效果,而这个数我们只需要给1向左移4位再按位取反就能得到。
通过上面几个例题,我们可以深刻体会到操作符背后强大的功能,而这些作用我们在没有深入学习之前可能根本想象不到。 操作符的作用是很大的,尤其是在嵌入式中,使用操作符来实现一些功能及其频繁。
5、逗号表达式
逗号表达式,就是用逗号隔开的多个表达式。逗号表达式从左向右依次执行,整个表达式的结果是最后一个表达式的结果。
注意:千万不要想当然的以为整个表达式的结果是最后一个表达式的结果就直接去算最后一个表达式,一定要从左到右每个表达式都执行。
另外,逗号表达式还有一个神奇的用法,来看示例:
这两个代码的效果是一样的,第二种运用了逗号表达式从左向右依次执行的特点,使代码更简单一些。
点击跳转主页—> 💥个人主页:小羊在奋斗