C语言中的运算符有很多种,这次我只对其中的移位运算符、位运算符和逻辑运算符做一总结。
*移位运算符*
移位运算符是将数据看成二进制数,对其bit位进行向左或向右移动若干位的运算符。移位运算符包括两种:左移运算符(<< )和右移运算符(>> ),均为双目运算符。第一个运算对象是移位对象,第二个运算对象是所移的二进制位数。
左移运算符(<<)
移位规则:左边“抛弃”,右边补0。如下图:
左移一位相当于给该数乘2,左移n位,即为给该数乘2的n次幂,但是此结论只适用于该数左移时被抛弃的高位中不包含1的情况。
右移运算符
右移运算符就比较复杂了,它分为两种:算数移位和逻辑移位,其移位规则当然会有异同。
算数移位:左边用移位对象的原符号位填充,右边丢弃。
逻辑移位:左边用0填充,右边丢弃。
接下来,以num=-1为例,对算数右移和逻辑右移进行区分:
int num = -1;
num = num >> 1;
从内存中可以看到:
&num(num在内存中的地址):0x00EFFD50,因为-1在内存中以“全1”的形式存放,故以十六进制形式显示即为:ff ff ff ff。将右移一位后的结果赋给num后,可以看到num的值并未改变(如下图),所以可知:在我所使用的编译器中,采用的是算数右移。
问题思考:移位计数(即移位操作的位数)允许的取值范围是什么?
问题解答:如果被移位的对象长度是n位,那么移位计数必须大于或等于0,且严格小于n。因此,不可能做到在单次操作中将某个数值中的所有位都移出。例如:若一个int型整数是32位,n是一个int型整数,那么n<<31和n<<0这样写是合法的,而n<<32和n<<-1这样写是非法的。
注意:
当需要对某整数(浮点数不能进行移位操作)进行除以2的n次幂的操作时,最好用右移n位进行替换,因为在计算机中计算除法更“费劲”,右移运算符更高效一点。
*位运算符*
位运算符包括:按位与(&)、按位或(|)、按位异或(^)、取反(~),它们的运算对象都必须为整数,而且除了取反运算符,其余三种均为双目运算符。
按位与(&)
按位与运算符”&”的功能是让参与运算的两个数相对应的的二进制位相与。
运算规则:只有对应的两个二进制位都为1时,结果位才为1;只要有一个二进制位为0,结果位就为0。举个例子:
int a = 3;
int b = 5;
int c = a&b;
按位或(|)
按位或运算符”|”的功能是让参与运算的两个数相对应的的二进制位相或。
运算规则:只有对应的两个二进制位都为0时,结果位才为0;只要有一个二进制位为1,结果位就为1。举个例子:
int a = 9;
int b = 10;
int c = a|b;
按位异或(^)
按位异或运算符”^”的功能是让参与运算的两个数相对应的的二进制位相异或。
运算规则:当对应的两个二进制位相同(都为1或都为0)时,结果位为0;当对应的两个二进制位相异时,结果位为1。上面的例子:
int a = 9;
int b = 10;
int c = a^b;
取反运算符(~)
取反运算符"~"用于求整数的二进制反码。
运算规则:二进制位为1的取反后变为0,二进制位为0的取反后变为1。
位运算符的应用:
⑴当想要知道一个整数的二进制序列中某位是0还是1,可以给该整数按位与1,便拿到了目标位。
⑵若想要将一个整数的二进制序列中某位的1置为0,可以只给该整数的对应位与0,而其他位按位与1。
⑶一个整数跟自己按位异或,结果为0。
其实,位运算符的应用还有很多。我们可以根据位运算符的特点适当地选择使用某个运算符来达到自己的目的。
*逻辑运算符*
逻辑运算符包括三种:逻辑与(&&)、逻辑或(||)和逻辑非(!)。“&&”和“||”是双目运算符,“!”是单目运算符。
逻辑与(&&)
运算规则:当“&&”两边的表达式的值只要有一个为假(0),那么整个逻辑表达式的值就为假;只有当两边的表达式的值都为真(非0)时,整个表达式的值才为真。
例:
int a = 10;
int b = 20;
if (a < b && a != 0)
{
printf("hello world");
}
如if中的表达式:a < b&&a!=0,左边表达式结果为真,右边的表达式也为真,故而if中整个表达式结果为真,便输出“hello world”。当把上面的代码改为:
int a = 10;
int b = 20;
if (a < b &&(b%a!=0))
{
printf("hello world");
}
此时,“&&”左边的表达式结果为真,但是右边的表达式结果为假(b%a的结果为0),所以if中整个表达式结果为假,“hello world”不会输出。
注意:当逻辑与左边的表达式结果为假时,右边的表达式不再进行求值和判断。
逻辑或(||)
运算规则:当“||”两边的表达式的值只要有一个为真(非0),那么整个逻辑表达式的值就为真;只有当两边的表达式的值都为假(0)时,整个表达式的值才为假。
还是上面的例子:
int a = 10;
int b = 20;
if (a < b ||(b%a!=0))
//……
左边的表达式为真,则右边的表达式不用再判断了,if中整个表达式结果为真。但是如果左边的表达式结果为假时,还应判断后面的表达式,当二者结果均为假时,if中整个表达式结果为假。
逻辑非(!)
运算规则:“!”后面的表达式结果为真(或假)时,“!表达式”的结果就为假(或真)。如:
a < b ||(b%a!=0)
当上式结果为真时,表达式“!(a < b ||(b%a!=0))”结果就为假。
当认识了位运算符和逻辑运算符后,现在来讲讲它们的区别。
逻辑表达式的结果不是0就是1,而按位与不一样。举个例子:1&2结果为0;而1&&2的结果为真(非0)。所以当我们如果期望输出一个字符串时,错误地将“1&&2”写成了“1&2”,便不能达到目的。所以应正确使用这些运算符,熟悉他们的特点和运算规则。