1. 開頭一問
本文開始前,先給出站長前幾天面試,一位面試官C語言大佬給的一題:
int i = 255;
i <<= 24;
i >>= 24;
問題:
- 最終
i等於多少? - 如果
i是uint類型,最終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. 總結及問題答案
看微軟文件位元運算子和移位運算子兩點注意
- 位元運算和移位運算永遠不會導致溢位,並且不會在已檢查和未檢查的上下文中產生相同的結果。
- 移位運算子僅針對
int、uint、long和ulong類型定義,因此運算的結果始終包含至少 32 位元。如果左側運算元是其他整數類型(sbyte、byte、short、ushort或char),則其值將轉換為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。這種右移操作被稱為算術右移。
- 對於無號整數的右移操作,不會保留符號位,而是將最高位的0也一同右移。這種右移操作被稱為邏輯右移。
本文如果有錯誤的地方歡迎指正,文中參考資料: