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也一同右移。這種右移操作被稱為邏輯右移。
本文如果有錯誤的地方歡迎指正,文中參考資料: