深入理解C#位元運算:掌握位元運算子的妙用

深入理解C#位元運算:掌握位元運算子的妙用

C#位元運算是一種強大的工具,可以在處理二進位資料和位元操作時發揮重要作用。透過使用位元運算子,我們可以對整數進行位元級別的操作,如位元AND、位元OR、位元XOR和位元NOT等。位元運算可以用於最佳化效能、壓縮資料、實作位元遮罩和位元旗標等。了解並掌握C#位元運算的基本原理與常見應用場景,將使我們能夠更高效地處理二進位資料,並在某些情況下提升程式碼的效能與可讀性。透過深入理解C#位元運算,我們可以在程式設計中發揮更大的創造力與靈活性。

最後更新 2023/11/6 下午11:15
沙漠尽头的狼
預計閱讀 9 分鐘
分類
.NET
標籤
.NET C# 位元運算

1. 開頭一問

本文開始前,先給出站長前幾天面試,一位面試官C語言大佬給的一題:

int i = 255;
i <<= 24;
i >>= 24;

問題:

  1. 最終i 等於多少?
  2. 如果iuint類型,最終i結果是多少?

2. C#位元運算

C#位元運算是一種強大的工具,可以在處理二進位資料和位元操作時發揮重要作用。透過使用位元運算子,我們可以對整數進行位元層級的操作,如位元與、位元或、位元互斥或和位元取反等。位元運算可以用於最佳化效能、壓縮資料、實現位元遮罩和位元旗標等。瞭解和掌握C#位元運算的基本原理和常見應用場景,將使我們能夠更高效地處理二進位資料,並在某些情況下提高程式碼的效能和可讀性。透過深入理解C#位元運算,我們可以在程式設計中發揮更大的創造力和靈活性。

本節內容主要參考文章:C# 中使用位元運算(與、或、非 & | ^)進行資料驗證c# 位元運算子_c#位元運算子-CSDN部落格

要學會位元運算,首先要清楚什麼是位元運算?程式中的所有內容在電腦記憶體中都是以二進位的形式儲存的(即:0或1),位元運算就是直接對在記憶體中的二進位數的每位進行運算操作。

在C#中可以對整型運算物件按位元進行邏輯運算。按位元進行邏輯運算的意義是:依序取被運算物件的每個位元,進行邏輯運算,每個位元的邏輯運算結果是結果值的每個位元。C#支援的位元邏輯運算子如表所示。

運算符號 意義 運算物件類型 運算結果類型 物件數 實例
~ 位元邏輯運算,按位元取反 整型,字元型 整型 1 ~a
& 位元邏輯運算,其實與&&邏輯運算子有一致的地方 同上 同上 2 a & b
| 位元邏輯運算,同樣與||有類似的地方 同上 同上 2 a | b
^ 位元邏輯互斥或運算 同上 同上 2 a ^ b
<< 位元左移運算 同上 同上 2 a<<4
>> 位元右移運算 同上 同上 2 a>>2

2.1. ~:位元邏輯非運算

位元邏輯非運算是單目的,只有一個運算物件。位元邏輯非運算按位元對運算物件的值進行非運算,即:如果某一位等於0,就將其轉變為1;如果某一位等於1,就將其轉變為0。

比如,對二進位的10010001進行位元邏輯非運算,結果等於01101110,用十進位表示就是:

145等於110;對二進位的01010101進行位元邏輯非運算,結果等於10101010。用十進位表示就是85等於176。

int a = 1001 0001;	// 十進位:145
int b = ~a;			// b = 0110 1110,即十進位:110

來複雜的,看這篇文章c# 位元運算子_c#位元運算子-CSDN部落格

int a = 13;
int b = -14;

Console.WriteLine(~a);	// -14
Console.WriteLine(~b);	// 13;

這個是怎麼操作二進位的呢,首先要記住一些原則:

原碼* 反碼 二補數** 取反
正數 符號位+絕對值 原碼 原碼 0、1對換
13 0 1101 0 1101 0 1101 1 0010
負數 符號位+絕對值 絕對值取反 反碼+1 0、1對換
-14 1 1110 1 0001 1 0010 0 1101

*:符號位的長度與類型定義有關,C#中int的符號位為1位 **:C#中數值以二補數儲存

以下展示兩者的原碼間如何轉換

int b	= 1 1110; // 前面的1表示符號位
反碼	= 1 0001; // 符號位不變
二補數	= 1 0010; // 反碼加1
二補數取反	= 0 1101; // 得到新的反碼就是結果a,連同符號位一起反)

int a = 0 1101;
a的二補數	= 0 1101;
二補數取反	= 1 0010; // 此時為b的二補數了
二補數轉反碼 = 1 0001; // 即減1
反碼轉原碼 = 1 1110; // 就是結果b的源碼

經過多次實驗得到一個規律就是:

~(+a)= -(a+1);(正數按位元取反只需要把當前數加1然後改成負的)

~(-a)= (+a-1);(負數按位元取反只需要把當前數先當做正數,然後減1得到結果)

2.2. $:位元邏輯與運算

位元邏輯與運算將兩個運算物件按位元進行與運算。與運算的規則:1與1等於1,1與0等於0。

範例程式碼:

int a = 13;
int b = 14;
int result = a & b;	// 12

轉換為二進位:

int a = 0000 1101;
int b = 0000 1110;
int result = 0000 1100;

&運算子會對二進位相同位置上面的0和1進行比對,當相同位置數字相同時就返回這個相同的數,否則就返回0,是不是和&&運算子判斷兩個bool 一致則返回True,否則返回False類似。所以我們得到了result這個結果,轉化成十進位就是12了。

2.3. |:位元邏輯或運算

位元邏輯或運算將兩個運算物件按位元進行或運算。或運算的規則是:1或1等1,1或0等於1,

範例程式碼:

int a = 13;
int b = 14;
int result = a | b;	// 15

轉換為二進位:

int a = 0000 1101;
int b = 0000 1110;
int result = 0000 1111;

其實判斷方式是一樣的,只是返回的結果不一樣, | 運算子判斷兩個二進位相同位置的0和1,只要其中一個位置的數字是1就返回1,是不是和 || 運算子同樣很類似,只要一個True則返True,再把得到的結果轉化成10進位就是15了。

2.4. ^:位元邏輯互斥或運算

位元邏輯互斥或運算將兩個運算物件按位元進行互斥或運算。互斥或運算的規則是:1互斥或1等於0,

1互斥或0等於1,0互斥或0等於0。即:相同得0,相異得1。

範例程式碼:

int a = 13;
int b = 14;
int result = a ^ b;	// 3

轉換為二進位:

int a = 0000 1101;
int b = 0000 1110;
int result = 0000 0011;

從中可以看出,^ 判斷相同位置上面的數字時,如果兩個數相同,不論是0還是1都返回0,如果其中一個為1就返回1。而 | 是只要有一個只要位置上一個為1就返回1,所以名字叫互斥或(不同的返回或)。

2.5. <<:位元左移運算

位元左移運算將整個數按位元左移若干位,左移後空出的部分0。比如:8位的byte型變數byte a=0x65(即二進位的01100101),將其左移3位:a<<3的結果是0x27(即二進位的00101000)。

byte a = 0110 0101;
a <<= 3;//0010 1000

將第一個運算元向左移動第二個運算元指定的位數,空出的位置補0。 左移相當於乘. 左移一位相當於乘2;左移兩位相當於乘4;左移三位相當於乘8。

 x<<1= x*2 
 x<<2= x*4 
 x<<3= x*8 
 x<<4= x*16

2.6. >>:位元右移運算

位元右移運算將整個數按位元右移若干位,右移後空出的部分填0。比如:8位的byte型變數byte a=0x65(既(二進位的01100101))將其右移3位:a>>3的結果是0x0c(二進位00001100)。

byte a = 0110 0101;
a >>= 3;//0000 1100

將第一個運算元向右移動第二個運算元所指定的位數,空出的位置補0。

右移相當於整除. 右移一位相當於除以2; 右移兩位相當於除以4; 右移三位相當於除以8。

 x>>1= x/2 
 x>>2= x/4 
 x>>3= x/8 
 x>>4= x/16

3. 總結及問題答案

看微軟文件位元運算子和移位運算子兩點注意

  1. 位元運算和移位運算永遠不會導致溢位,並且不會在已檢查和未檢查的上下文中產生相同的結果。
  2. 移位運算子僅針對 intuintlongulong 類型定義,因此運算的結果始終包含至少 32 位元。如果左側運算元是其他整數類型(sbytebyteshortushortchar),則其值將轉換為 int 類型。

這裡給出文章開頭問題的答案及答案解析結束本文:

int i = 255;	// 00000000 00000000 00000000 11111111
i <<= 24;		// 11111111 00000000 00000000 00000000
i >>= 24;		// 11111111 11111111 11111111 11111111;

修改i資料類型為uint

uint i = 255;	// 00000000 00000000 00000000 11111111
i <<= 24;		// 11111111 00000000 00000000 00000000
i >>= 24;		// 00000000 00000000 00000000 11111111;

int和uint位移後結果不同的原因:

  1. 對於有號整數的右移操作,會將最高位的符號位也一同右移。這意味著如果原始數值的最高位是1,那麼右移後,符號位會被保留,即填充1。這種右移操作被稱為算術右移。
  2. 對於無號整數的右移操作,不會保留符號位,而是將最高位的0也一同右移。這種右移操作被稱為邏輯右移。

本文如果有錯誤的地方歡迎指正,文中參考資料:

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2026/2/7

AOT使用經驗總結

從專案建立伊始,就應養成良好的習慣,即只要添加了新功能或使用了較新的語法,就及時進行 AOT 發布測試。

繼續閱讀