[史上最全]C#(VB.NET)中位运算符工作过程剖析(译)

简介:

原文地址CodeProject

目录

介绍

在这篇博客中,我将来讨论与位操作符有关的内容。这篇文章中谈到的位操作符有:

  • 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为例):

  1. 先得到783的二进制:0000001100001111(前面空白补0)
  2. 按位取反得到:1111110011110000
  3. 然后加1得到:1111110011110001
  4. 那么,-783的二进制为1111110011110001
  5. 怎么确定得到的结果是一个负数呢?这主要依赖于数据类型。如果数据类型为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为例):

  1. 将原数按位取反得到:0000000000101100
  2. 将取反后的结果转换成十进制:44
  3. 将44加1得到45
  4. 将45变为负数:-45
  5. 最后,负二进制数值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]OCK]]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类型。

作者:周见智 
出处:http://www.cnblogs.com/xiaozhi_5638/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类:  E文翻译

本文转自周见智博客博客园博客,原文链接:http://www.cnblogs.com/xiaozhi_5638/p/4562719.html,如需转载请自行联系原作者
目录
相关文章
|
1月前
|
算法 C# 数据库
【干货】一份10万字免费的C#/.NET/.NET Core面试宝典
C#/.NET/.NET Core相关技术常见面试题汇总,不仅仅为了面试而学习,更多的是查漏补缺、扩充知识面和大家共同学习进步。该知识库主要由自己平时学习实践总结、网上优秀文章资料收集(这一部分会标注来源)和社区小伙伴提供三部分组成。该份基础面试宝典完全免费,发布两年来收获了广大.NET小伙伴的好评,我会持续更新和改进,欢迎关注我的公众号【追逐时光者】第一时间获取最新更新的面试题内容。
|
1月前
|
数据可视化 网络协议 C#
C#/.NET/.NET Core优秀项目和框架2024年3月简报
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
|
2月前
|
SQL 数据库 C#
C# .NET面试系列十一:数据库SQL查询(附建表语句)
#### 第1题 用一条 SQL 语句 查询出每门课都大于80 分的学生姓名 建表语句: ```sql create table tableA ( name varchar(10), kecheng varchar(10), fenshu int(11) ) DEFAULT CHARSET = 'utf8'; ``` 插入数据 ```sql insert into tableA values ('张三', '语文', 81); insert into tableA values ('张三', '数学', 75); insert into tableA values ('李四',
74 2
C# .NET面试系列十一:数据库SQL查询(附建表语句)
|
2月前
|
开发框架 算法 搜索推荐
C# .NET面试系列九:常见的算法
#### 1. 求质数 ```c# // 判断一个数是否为质数的方法 public static bool IsPrime(int number) { if (number < 2) { return false; } for (int i = 2; i <= Math.Sqrt(number); i++) { if (number % i == 0) { return false; } } return true; } class Progr
62 1
|
2月前
|
并行计算 安全 Java
C# .NET面试系列四:多线程
<h2>多线程 #### 1. 根据线程安全的相关知识,分析以下代码,当调用 test 方法时 i > 10 时是否会引起死锁? 并简要说明理由。 ```c# public void test(int i) { lock(this) { if (i > 10) { i--; test(i); } } } ``` 在给定的代码中,不会发生死锁。死锁通常是由于两个或多个线程互相等待对方释放锁而无法继续执行的情况。在这个代码中,只有一个线程持有锁,且没有其他线程参与,因此不
114 3
|
10天前
|
开发框架 .NET 中间件
C#/.NET快速上手学习资料集(让现在的自己不再迷茫)
C#/.NET快速上手学习资料集(让现在的自己不再迷茫)
|
2天前
|
XML 开发框架 .NET
C#/ASP.NET应用程序配置文件app.config/web.config的增、删、改操作
C#/ASP.NET应用程序配置文件app.config/web.config的增、删、改操作
|
26天前
|
开发框架 前端开发 JavaScript
采用C#.Net +JavaScript 开发的云LIS系统源码 二级医院应用案例有演示
技术架构:Asp.NET CORE 3.1 MVC + SQLserver + Redis等 开发语言:C# 6.0、JavaScript 前端框架:JQuery、EasyUI、Bootstrap 后端框架:MVC、SQLSugar等 数 据 库:SQLserver 2012
22 0
|
1月前
|
开发框架 人工智能 .NET
C#/.NET/.NET Core拾遗补漏合集(持续更新)
在这个快速发展的技术世界中,时常会有一些重要的知识点、信息或细节被忽略或遗漏。《C#/.NET/.NET Core拾遗补漏》专栏我们将探讨一些可能被忽略或遗漏的重要知识点、信息或细节,以帮助大家更全面地了解这些技术栈的特性和发展方向。
|
2月前
|
SQL 存储 关系型数据库
C# .NET面试系列十:数据库概念知识
#### 1. 为什么要一定要设置主键? 设置主键是数据库设计中的一个重要概念,有几个主要原因: 1、唯一性 ```c# 主键必须保证表中的每一行都有唯一的标识。这样可以避免数据冗余和不一致性。如果没有主键或者主键不唯一,就可能出现数据混乱或错误。 ``` 2、查询性能 ```c# 数据库系统通常会使用主键来加速数据检索。主键通常会被索引,这样可以更快速地找到特定行的数据,提高查询效率。 ``` 3、关联性 ```c# 主键常常用于建立表与表之间的关系。在关系数据库中,一个表的主键通常与其他表中的外键建立关联,这种关系对于数据的一致性和完整性非常重要。 ``` 4、数据完
134 1
C# .NET面试系列十:数据库概念知识