C#教程(7)———— 位运算符

前言

在之前我们一共介绍了算术运算符、赋值运算符、比较运算符和逻辑运算符四种运算符,而在运算符大家庭中还有一个较为独特的运算符类型,称为位运算符,那么什么是位运算符,又有哪些位运算符呢?

1 位运算符

位运算符是一种较为特殊的运算符,和之前介绍的四种运算符不同的是,位运算符直接作用于位(bit),并依次按位进行操作,而我们也知道,在计算机中所有的数据都是以二进制的形式保存的,而非十进制,因此,位运算符的使用不能再像以往一样在十进制的角度进行考虑了,它的操作都是以二进制位来进行处理的,所以在理解上还会有一定的难度。

在C#中位运算符一共有6个:

  • 按位求补运算符或称为按位取反运算符,符号为~
  • 左移运算符,符号为<<
  • 右移运算符,符号为>>
  • 按位与运算符,符号为&
  • 按位或运算符,符号为|
  • 按位异或运算符,符号为^

1.1 按位取反运算符

按位取反运算符是一个一元运算符,也可以将它理解为按位否运算符,它会将每一位上的二进制数由1变为0或由0变为1,不过在之前我们也有提到过,计算机存储负数或正数的时候,是占用了每一个数据的首位作为符号位的,第一位是1表示负数,是0表示正数,而按位取反运算符会连带着第一位的符号位一起改变,也就是说经过这个运算,正数会变成负数,负数会变为正数,那么我们可以看一下以下的代码。

int num1 = 14;
int num2 = -15;
Console.WriteLine(~num1);//输出结果为-15
Console.WriteLine(~num2);//输出结果为14

神奇的事情发生了,数据经过了按位取反运算,符号发生了变化没错,但为什么14取反后不是-14,而是-15呢?这个数据又是怎么来的?

1.1.1 原码,反码和补码

这里就涉及到计算机到底是如何存储负数这个问题了,那么我们来想一下,根据之前所说的,用第一位表示符号,后面表示数字,最简单的正负数存储方法是什么样的。

比如用1来举例,就以sbyte类型来说,一共有八位,第一位为符号位,那么+1的二进制存储形式就是这样的:0000 0001,那-1呢,只是符号变了数值没变,那是不是就可以用:1000 0001呢?

事实上并不是,这种思路明明更加清晰,为什么没被采用呢?原因很简单,就是因为数字中有一个很特殊的存在,那就是0,二进制的0保存下来是什么样的:0000 0000,但采用刚才的保存方式,会出现一个特殊的二进制值:1000 0000,也就是-0的概念,我们都知道在数学中,0的正负是不需要考虑的,那也就是说这种方式会在计算机中造出一个-0,这就有一定的问题了,那么我们是怎么解决-0这个问题的呢?

就是通过补码,那么这里就需要解释三个新的概念了,原码、反码和补码。

-原码:数据的符号位加上数据本身的绝对值构成原码,简单来说就是按照刚才我们所说的那种形式保存的数据本身

+1的原码: 0000 0001
-1的原码: 1000 0001

  • 反码:正数的反码是其本身,负数的反码是在原码的基础上,符号位不变,其它位全部取反

[+1] = [0000 0001] = [0000 0001]
[-1] = [1000 0001] = [1111 1110]

  • 补码:正数的补码是其本身,负数的补码是在反码的基础上加1

[+1] = [0000 0001] = [0000 0001] = [0000 0001]
[-1] = [1000 0001] = [1111 1110] = [1111 1111]

根据上面的描述,我们也能看到,反码和补码主要影响的就是负数,而计算机在存储数据时存储的实际上就是补码,我们可以再来想想-0的问题,如果是-0的话,补码会变成什么呢?

[-0] = [1000 0000] = [1111 1111] = [0000 0000]

经过补码的运算,-0也变成了和+0一样的数字了,借助这个方法,就可以避免正负0的问题了。

那么除了正负0外,补码还解决了什么问题呢?

那就是减法的问题,计算机和人脑不同,人脑可以轻易理解减法,但计算机直接理解减法会是个很复杂的情况,也因此,计算机在计算减法时实际上是把减号后的数字当做负数运算的,也就是说 [1 - 1]这个式子,计算机实际上的运算是 [1 + (-1)],那么这样的运算在使用原码时会出什么问题呢?

1 - 1 = 1 + (-1) = [0000 0001] + [1000 0001] = [1000 0010] = - 2

可以看到使用原码运算时,[1 - 1]的运算结果变成了-2,显然是不正确的,但如果使用补码呢?

1 - 1 = 1 + (-1) = [0000 0001] + [1111 1111] = [0000 0000] = 0

这下就没有问题了,感兴趣的朋友也可以自己用别的数字试验一下哦。

1.1.2 按位取反运算结果

那么我们再回到按位取反运算符,来看一下14和-15两个数在经过取反时经历了什么。

~14 = ~[0000 1110] = [1111 0001] = [1111 0000] = [1000 1111] = -15
~(-15) = ~[1111 0001] = [0000 1110] = [0000 1110] = [0000 1110] = 14

这就是按位取反在运算中的经过了。

1.2 移位运算符

移位运算符是一个二元运算符,我们先来看运行结果:

Console.WriteLine(14 << 1);//输出结果为28
Console.WriteLine(14 >> 1);//输出结果为7

看起来是不是很奇怪,这个运算符到底做了什么呢?我们直接来看原理

14 = 0000 1110
14 <<1 = 0001 1100 = 28
14>>1 = 0000 0111 = 7

看明白了么?其实也可以通过移位运算符这个名字来看出来,实际上这两个运算符就是将数据的二进制向左或向右移动了,比如14<<1就是将14的二进制数向左移动一位,将左边移出去的数据抛弃,在右边多出来的位置上补0。

能理解左移的话右移也就很好理解了,就是反过来将数据向右移位,右侧移出去的数据抛弃左侧多出的空位补0。

1.3 按位与、或、异或运算符

这三个运算符中前两个大家看起来应该不陌生,就是上期我们提到的逻辑运算符,只是只保留了一半,那么在这里又会有什么样的效果呢?

1.3.1 按位与运算符(&)

我们先来看示例:

Console.WriteLine(13 & 14);//输出结果为12

结果是不是很莫名其妙,那我们还是放到二进制中来看

13 = 0000 1101
14 = 0000 1110
12 = 0000 1100

发现规律了么,和逻辑与运算符(&&)类似,按位与运算符(&)是根据二进制位,如果两个数这一位上的数据都是1的话,那么会返回1,如果有一个0或者全是0的话,就会返回0,所以13和14经过运算后,就变成了12这个数字。

1.3.2 按位或运算符(|)

一样先看演示

Console.WriteLine(13 | 14);//输出结果为15

我们再来看二进制数据找一下规律。

13 = 0000 1101
14 = 0000 1110
15 = 0000 1111

没错,和逻辑或运算符(||)类似,按位或运算符(|)也是两个数在这一位上,如果有一个为1就返回1,只有全是0的时候才返回0。

较为特殊的是,和之前的按位取反,移位等位运算符只能应用于整数或者能转换为整数的char类型不同,按位与和按位或两个运算符还可以应用于bool类型,进行逻辑判断,也就是说这两个运算符既可以作为位运算符,又可以 作为逻辑运算符。
true | false = true
true & false = false
以上两种形式也是合法的

1.3.3 按位异或运算符(^)

异或是一个较为新奇的概念了,同样我们还是先来看示例。

Console.WriteLine(13 ^ 14);//输出结果为3

这次的数据更加奇怪了,放到二进制中来看一下呢?

13 = 0000 1101
14 = 0000 1110
3 = 0000 0011

找到规律了么?感觉和或有点类似但又有点不一样,什么是异或呢?

  • 异或:判断两个数相同时返回0,不同时返回1

和或不同,当两个数据相同的时候,无论都是1还是都是0,异或都会返回0,而或在两个数值都为1的时候返回的是1,这也就是异或和或的区别了,所以它叫做异或。

1.4 复合运算符

今天提到的所有运算符都可以和赋值运算符形成复合运算符。

a^=b 等价于 a = a^b
a&=b 等价于 a = a&b

总结

以上就是今天的所有内容了,今天主要介绍的是c#中的六种位运算符,而因为位运算符的运算涉及到二进制数据,所以在理解中还是有一定难度的,也希望大家能够认真理解。

  • 15
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值