[史上最全]C#(VB.NET)中位运算符工作过程剖析(译)
目录
介绍
在这篇博客中,我将来讨论与位操作符有关的内容。这篇文章中谈到的位操作符有:
- OR(C#中使用“|”,VB.NET中使用Or)
- AND(C#中使用“&”,VB.NET中使用And)
- XOR(C#中使用“^”,VB.NET中使用Xor)
- NOT(C#中使用“~”,VB.NET中使用Not)
- 左移运算符(C#和VB.NET中都使用<<)
- 右移运算符(C#和VB.NET中都使用>>)
- 循环按位移动
- 循环左移(C#和VB.NET中没有对应的运算符)
- 循环右移(C#和VB.NET中没有对应的运算符)
位操作符一般用在数值类型上,它作用在数字二进制格式的每一位上(0和1),所以我们先要搞清楚十进制和二进制的相互转换。这篇文章开头我会给出一些(二进制-十进制)转换示例,虽然都是以Byte类型进行说明的,但其他诸如Int32、Int16等数值类型转换的原理是一样的。
位操作符的使用不仅仅只在C#和VB.NET两种语言中,本篇文章只以这两种语言举例。
“二进制-十进制”相互转换
这一节中我将介绍有关十进制与二进制相互转换的内容。
十进制->二进制
假设我们有一个十进制数字783,我们可以使用下面的方法将其转换成二进制:
|
除法: |
783 / 2 |
391 / 2 |
195 / 2 |
97 / 2 |
48 / 2 |
24 / 2 |
12 / 2 |
6 / 2 |
3 / 2 |
1 / 2 |
|
商: |
391 |
195 |
97 |
48 |
24 |
12 |
6 |
3 |
1 |
0 |
|
余数: |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
当商为0时,我们停止计算。现在我们从右往左拼接每一步得到的余数,我们会得到1100001111。
按照下面方式可以将一个负十进制数转换为二进制(以-783为例):
- 先得到783的二进制:0000001100001111(前面空白补0)
- 按位取反得到:1111110011110000
- 然后加1得到:1111110011110001
- 那么,-783的二进制为1111110011110001
- 怎么确定得到的结果是一个负数呢?这主要依赖于数据类型。如果数据类型为Int16,那么第一位若为0,则为正数,否则为负数。如果数据类型是不带符号的,比如UInt16,那么第一位数不代表符号,1111110011110000就是十进制的64752。
二进制->十进制
如果你有一个二进制数字0000000100010110(Int16),现将每个位的顺序颠倒(你会得到0110100010000000),然后使用以下方法:
|
位b: |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
|
b * 2n |
0 * 20 |
1 * 21 |
1 * 22 |
0 * 23 |
1 * 24 |
0 * 25 |
0 * 26 |
0 * 27 |
1 * 28 |
0 * 29 |
0 * 210 |
0 * 211 |
0 * 212 |
0 * 213 |
0 * 214 |
0 * 215 |
|
结果: |
0 |
2 |
4 |
0 |
16 |
0 |
0 |
0 |
256 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
最后将每一步得到的结果相加:

按照下面方式可以将一个负二进制数值转换成十进制(以1111111111010011为例):
- 将原数按位取反得到:0000000000101100
- 将取反后的结果转换成十进制:44
- 将44加1得到45
- 将45变为负数:-45
- 最后,负二进制数值1111111111010011的十进制格式为-45
OR运算符(按位或|)
OR运算符工作方式
假设现在有两个Byte类型的数38和53,那么我们先将它们转换成二进制格式:

按照下表的方式:
|
A |
0 |
0 |
1 |
0 |
0 |
1 |
1 |
0 |
|
B |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
|
A | B (A Or B) |
0 |
0 |
1 |
1 |
0 |
1 |
1 |
1 |
如果两个数是Int16类型的,那么它们就有可能是负数。一个负数和一个正数按位或运算后得到的结果还是负数(第一位肯定是1),因此,-15|378(VB.NET中-15 Or 378)的结果为-5。
C#和VB.NET中的按位或运算符的使用,参见下面代码:
[C#]

[VB.NET]

FlagsAttribute
通过使用FlagsAttribute,你可以将枚举类型的每个值都当作二进制中的位(1和0),当然在定义枚举类型的时候有要求,即每个枚举值必须按照1、2、4、8(2的N次方)这样的规律初始化。
[C#]

[VB.NET]

现在你可以使用按位或运算符来操作枚举类型:
[C#]

[VB.NET]

AND运算符(按位与&)
假设有两个数76和231,我们现将它们转换成二进制:

然后按照下表计算:
|
A |
0 |
1 |
0 |
0 |
1 |
1 |
0 |
0 |
|
B |
1 |
1 |
1 |
0 |
0 |
1 |
1 |
1 |
|
A & B (A And B) |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
仅仅当A和B都为负时,A&B(VB.NET中的A And B)的结果才为负,其他情况下结果都为正(只有A和B的第一位都为1时,结果的第一位才为1)。
C#和VB.NET中的按位与运算符的使用,参见下图:
[C#]

[VB.NET]

XOR运算符(按位异或^)
XOR运算符的工作方式
按位或OR运算符不同于按位异或XOR运算符。如果你使用按位或OR,那么1|1(VB.NET中的1 Or 1)的结果为1,但是如果你使用按位异或XOR,那么1^1(VB.NET中的1 Xor 1)的结果为0。仅仅在1^0或者0^1时,结果才为1。
假设你有两个数值138和43,那么现将它们转换为二进制格式:

然后按照下表:
|
A |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
|
B |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
|
A ^ B (A Xor B) |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
C#和VB.NET中的按位异或运算符的使用,参见下面代码:
[C#]

[VB.NET]

使用XOR交换变量值的算法
使用XOR运算符可以交换两个变量值,并且不需要中间临时变量做辅助:
[C#]

[VB.NET]

使用XOR加密
在XOR运算符的帮助下,你可以给一个文本加密。遍历文本的每个字符,然后使用XOR运算符c ^ k(VB.NET中的c Xor k)生成新的字符。其中k就是一个整数值。
[C#]

[VB.NET]

最终输出结果为zFG]♫G]♫O♫CK]]OIK。这种方式加密非常容易被破解,所以最好不要使用单一的字符(比如k),我们可以使用一串文本:
[C#]

[VB.NET]

最终的输出为m_☻\ D+♫.↓Z♫\SL?Ka。现在破解这个加密算法相对来讲要复杂一些,但是这种方式还不是很保险,如果别人知道了你的key(代码中的k字符串),那么破解起来相当简单。因此,不要使用XOR这种方式作为加密的单一算法,如果你对安全、加密感兴趣,你可以结合其他的一些加密方式,将XOR运算符应用到其中,作为整个加密过程的一部分。
NOT运算符(按位非~)
按位非操作符NOT将会改变二进制中每位的值,0变为1,1变为0。如果一个数值有符号,那么整数经过运算后会变成负数,负数经过变换后会变为正数。如果数值没有符号,那么永远都为正(0除外)。假设你有一个数值52(二进制00110100,Byte类型,无符号),那么~52(VB.NET中的Not 52)的计算方式为:
|
A |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
|
~A |
1 |
1 |
0 |
0 |
1 |
0 |
1 |
1 |
将11001011转换成十进制为203,所以~52(Byte类型)的值为203。
C#和VB.NET中按位非运算符的使用,参见下面代码:
[C#]

[VB.NET]

左移运算符(<<)
左移运算符工作方式
x<<n表示将X的二进制格式中的每位向左移动n个位置,右边空出来的位置补0。

如图所示,每位均向左移动1个位置,右边空出来的位置补0。所以154<<1等于52。154<<n的值参见下表:
|
154 << 0 (= 154) |
1 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
|
154 << 1 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
|
154 << 2 |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
|
154 << 3 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
|
154 << 4 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
|
154 << 5 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
|
154 << 6 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
|
154 << 7 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
|
154 << 8 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
C#和VB.NET中左移运算符的使用,参见下面代码:
[C#]

[VB.NET]

使用左移运算符计算2的幂
1<<n的值为(2的n次方),但是使用这种方式计算2的幂要比使用Math.Pow更快:
[C#]

[VB.NET]

右移运算符(>>)
右移运算符工作方式
x>>n表示将x的二进制格式的每位均向右移动n个位置,左边空出来的位置补0(与左移相反)。

如上图所示,每位均向右移动1个位置。所以155>>1的值为77。注意如果为负数,那么它的符号会被隐藏掉。
下表显示的是计算155>>n的值:
|
155 >> 0 |
1 |
0 |
0 |
1 |
1 |
0 |
1 |
1 |
|
155 >> 1 |
0 |
1 |
0 |
0 |
1 |
1 |
0 |
1 |
|
155 >> 2 |
0 |
0 |
1 |
0 |
0 |
1 |
1 |
0 |
|
155 >> 3 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
1 |
|
155 >> 4 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
|
155 >> 5 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
|
155 >> 6 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
|
155 >> 7 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
|
155 >> 8 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
C#和VB.NET中的右移运算符的使用,参见下面代码:
[C#]

[VB.NET]

使用右移运算符计算x/(2的幂)
x>>n的值等于x/(2的n次方),比如8>>2的值为8/(2的2次方),也就是8/4。
[C#]

[VB.NET]

当然,这种方式的计算速度也要高于8/Math.Pow(2,2);
[C#]

[VB.NET]

循环按位移动
循环按位左移
循环按位左移会将数值的二进制格式中的每位均向左移动1个位置,然后将移出来的数值(1或0)替补到右边空白处。

上图显示了将154循环按位向左移动1位,它的值等于154<<1|154>>7。循环按位左移得到的结果可以归纳为:a<<n|a>>(b-n)。b为数值的位数,如果数值为Byte类型,那么最后的结果为a<<n|a>>(8-n),如果数值为Int32类型,那么b为32,最后的结果为a<<n|a>>(32-n)。
C#和VB.NET中循环按位左移的使用,可以参见下面:
[C#]

[VB.NET]

循环按位右移
循环按位右移会将数值的二进制格式的每位均向右移动1个位置,然后将移出来的数值(1或0)替补到左边空白处。

如上图所示,将155循环按位右移1个位置,最后它的值等于155>>1|155<<7。循环按位右移得到的结果可以归纳为:a>>n|a<<(b-n)。其中b为数值位数。如果数值为Byte类型,那么结果为a>>n|a<<(8-n),如果数值为Int32类型,那么得到的结果为a>>n|a<<(32-n)。
C#和VB.NET中循环按位右移的使用,可以参见下面代码:
[C#]

[VB.NET]

译者注:在使用位操作符时,一定要先确定被操作的数值是什么类型,占多少位,同一个数值,数据类型不同,最后得到的结果不一样。原文中,对于任何一个数值(比如52),都在强调它是Byte类型还是Int16类型。
[史上最全]C#(VB.NET)中位运算符工作过程剖析(译)的更多相关文章
- webpack之前端性能优化(史上最全,不断更新中。。。)
最近在用webpack优化首屏加载性能,通过几种插件之后我们上线前后的速度快了一倍,在此就简单的分享下吧,先上个优化前后首屏渲染的对比图. 可以看到总下载时间从3800ms缩短到1600ms. 我们在 ...
- [转] webpack之前端性能优化(史上最全,不断更新中。。。)
最近在用webpack优化首屏加载性能,通过几种插件之后我们上线前后的速度快了一倍,在此就简单的分享下吧,先上个优化前后首屏渲染的对比图. 可以看到总下载时间从3800ms缩短到1600ms. 我们在 ...
- 史上最全的LaTeX特殊符号语法
史上最全的LaTeX特殊符号语法 运算符 语法 效果 语法 效果 语法 效果 + \(+\) - \(-\) \triangleleft \(\triangleleft\) \pm \(\pm\) \ ...
- 史上最全的CSS hack方式一览 jQuery 图片轮播的代码分离 JQuery中的动画 C#中Trim()、TrimStart()、TrimEnd()的用法 marquee 标签的使用详情 js鼠标事件 js添加遮罩层 页面上通过地址栏传值时出现乱码的两种解决方法 ref和out的区别在c#中 总结
史上最全的CSS hack方式一览 2013年09月28日 15:57:08 阅读数:175473 做前端多年,虽然不是经常需要hack,但是我们经常会遇到各浏览器表现不一致的情况.基于此,某些情况我 ...
- 史上最全的spark面试题——持续更新中
史上最全的spark面试题——持续更新中 2018年09月09日 16:34:10 为了九亿少女的期待 阅读数 13696更多 分类专栏: Spark 面试题 版权声明:本文为博主原创文章,遵循C ...
- 史上最全Windows版本搭建安装React Native环境配置
史上最全Windows版本搭建安装React Native环境配置 配置过React Native 环境的都知道,在Windows React Native环境配置有很多坑要跳,为了帮助新手快速无误的 ...
- 【Tips】史上最全H1B问题合辑——保持H1B身份终级篇
[Tips]史上最全H1B问题合辑——保持H1B身份终级篇 2015-04-10留学小助手留学小助手 留学小助手 微信号 liuxue_xiaozhushou 功能介绍 提供最真实全面的留学干货,帮您 ...
- 开源框架】Android之史上最全最简单最有用的第三方开源库收集整理,有助于快速开发
[原][开源框架]Android之史上最全最简单最有用的第三方开源库收集整理,有助于快速开发,欢迎各位... 时间 2015-01-05 10:08:18 我是程序猿,我为自己代言 原文 http: ...
- 史上最全的CSS hack方式一览
做前端多年,虽然不是经常需要hack,但是我们经常会遇到各浏览器表现不一致的情况.基于此,某些情况我们会极不情愿的使用这个不太友好的方式来达到大家要求的页面表现.我个人是不太推荐使用hack的,要知道 ...
随机推荐
- [转]DDD领域驱动设计基本理论知识总结
领域驱动设计之领域模型 加一个导航,关于如何设计聚合的详细思考,见这篇文章. 2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity i ...
- jq 模板
菜鸟教程1.4.6版本angularJS <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js ...
- 2.View绘制分析笔记之onMeasure
今天主要学习记录一下Android View绘制三部曲的第一步,onMeasure,测量. 起源 在Activity中,所有的View都是DecorView的子View,然后DecorView又是被V ...
- android 查看当前正在运行的进程
转载至 https://github.com/wenmingvs/AndroidProcess 因为Android5.0以上的权限封锁,无法直接获取正在运行的进程,此文总共介绍6中方法获取, 详细介绍 ...
- java提升路线书单(原文自知乎刘欣)
复制黏贴自知乎刘欣大神,作为个人的书单与指导路线 原文链接:https://www.zhihu.com/question/19848946/answer/92536822 刘欣 追寻内心的真正兴趣 ...
- "不能在 DropDownList 中选择多个项。"其解决办法及补充
探讨C#.NET下DropDownList的一个有趣的bug及其解决办法 摘要: 本文就C#.Net 环境下Web开发中经常使用的DropDownList控件的SelectedIndex属性进行了详细 ...
- WebApi防重复提交方案
使用Redis锁机制. 偽代碼: void post { var key = GetKey(); var value = Redis.Incre(key); if(value == 1) { var ...
- 历年NOIP水题泛做
快noip了就乱做一下历年的noip题目咯.. noip2014 飞扬的小鸟 其实这道题并不是很难,但是就有点难搞 听说男神错了一个小时.. 就是$f_{i,j}$表示在第$i$个位置高度为$j$的时 ...
- python基础06 循环
循环用于重复执行一些程序. for循环 for循环需要预先设定循环的次数n,然后执行隶属于for的语句. 基本构造是 for 元素 in 序列: statement 如: for a in [1 ...
- 伪Textatea的构建(div+table),以及相应的滚动条问题与safari上的优化
在页面中创建一个不可编辑的文本块,并且文本块的篇幅较大,第一反应是创建一个textarea,并将它的disabled="disabled",并设置相应的scroll属性,就可以构建 ...