[史上最全]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]♫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類型。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM