關於操作符/位操作 的操作方法和應用 附加(整形提升)(C語言)

關於操作符/位操作 的操作方法和應用 附加(整形提升)(C語言)

1.瞭解什麼是位操作(二進制數,位)
2.關於位操作符的詳解( ‘&’ ‘|’ ‘~’ )
3.位移操作符(">>","<<")
4.位異或的一些妙用


Hellolo
關於這一章的話,之所以想成寫一篇博客是因爲這一章在我們學校高級語言程序課中基本就是草草概括,正巧寒假開始數據結構,也在Leetcode上了解到可以巧妙運用位操作來解決一些時間空間”雙殺“的問題。
所以,一鼓作氣,寫下這篇Blog,也好讓自己鞏固這一方面知識。

1.位操作
相信大家對於操作符都不陌生,每個語言都有着自己的操作符,在C語言中,操作符分爲:
算術、位移、位、賦值、單目、三目(條件)、關係、邏輯、逗號 操作符
而本篇blog主要介紹位操作符:
要想知道位操作符,首先得知道什麼是”位“
瞭解“位”之前,我覺得很有必要了解一下“二進制位計算法”
進制
很多時候,身邊沒有計算機的情況下,如果我們要轉換進制數,其實是一件非常簡單的一件事。舉個栗子,我們生活中最常使用的十進制數字:1729 來說
我們可以這樣轉化:每個數字都有着各自的權重 ,9是個位,2是十位,7是百位,1是千位,所以 1729 = 11000 + 7100 + 210 + 91
當然這種方法我個人不喜歡,不如換一個方法,這個方法專業叫法是”以N爲基底書寫XXX“(這裏的N爲進制數,XXX爲你要轉化的數字)
就拿剛剛的例子,就爲,”以10爲基底書寫1729“
那麼好了,這樣就是:1729 = 1 * 10^3 + 1 * 10^2 + 1 * 10^1 + 1*10^0










這是我們的10進制
但是機器可就沒我們這麼厲害了,它只識得1和0 (關閉或者打開)
這也就導致我們經常在編程過程中運用最多的是二進制

但是瞭解了剛剛的”基底書寫“,二進制的轉化也很簡單不是嗎?
eg:將二進制101轉化成十進制?
那麼就是 1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 5

現在我們都知道了,機器相對於聰明的人類來講,思考和計算還是比較喫力的。但身爲一個程序員,我們必須要掌握如何利用二進制系統來表示1字節和整數。

比如,在現實生活中我出去買零食,10元的奶茶+5元的薯片 讓我剛剛踏入幼兒園的表弟來算都知道 10 + 5 = 15 需要15元
而計算機則不然,它是這樣算的*(這裏爲了好理解,用C語言來解釋)*:

int a = 10;
int b = 5;
int c = a + b;
//最終需要c,這麼多的錢 

當然了,如果真是眼看着這樣的話,那我剛剛說的都白說了
實際上:

int a = 10;
//10 -轉化爲二進制
//00000000000000000000000000001010 - 10
int b = 5;
//5 - 轉化爲二進制
//00000000000000000000000000000101 - 5
int c = a + b;
//00000000000000000000000000001010
//00000000000000000000000000000101
//上述兩式子相加後得到
//00000000000000000000000000001111 - 1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 = 15

這就是計算機運用的二進制計算的原理

這就引出了以下”三匹馬”(個人覺得有意思就這麼想出了這種記法~)
原碼、補碼、反碼
上述代碼中
將10轉化爲二進制後得到的就是該數字的*“原碼”*
即00000000000000000000000000001010 - 10的原碼
而存於計算機內存中的卻不是原碼
而是補碼,補碼很重要!因爲存在計算機內存中,所以計算和處理的時候,都是補碼在工作!
補碼怎麼得到呢?(這裏拿int整數來舉例子)
這裏要看這個數字的正負了(這裏由於太深奧,總的來說,有符號的整數取決於硬件,和各種語言沒任何關係。具體嘛。。我也不知道~~~QAQ)
這裏引出一個叫做 符號量(sign - magnitude) 的表示法 即正數符號位爲0,負數符號位爲1。
在這基礎上:
1.正整數 —> 原碼反碼補碼統統相同
2.負整數 —> 原碼反碼補碼需要通過計算得到 (原碼–>反碼–>補碼)











這裏就不繼續解釋這“三匹馬”了,停!
讓我們趕緊回到剛剛的代碼中,現在我們曉得了,正整數的原反補都是一樣的,那麼就好辦了,把補碼兩者相加,得到的即就是c的補碼–>原碼
00000000000000000000000000001010 ----10的補碼
00000000000000000000000000000101 ---- 5的補碼


=
00000000000000000000000000001111 — 15的補碼 也即原碼

這樣大家就都明白了吧~~
但是這個時候就有人問了,我需要知道這玩意幹啥!?
emmm說實話一開始我也覺得知道這玩意好像沒啥用,但是你看看下面的代碼,看看你能不能一下子算出結果~

char a = 3;
char b = 127;
char c = a + b;
printf("%d ",c);
//C爲多少?

哦!對!還要再講一個東西!不然這題解不了。
叫做————整型提升

表達式的整形原型要在CPU的相應運算期間內執行,CPU內整形運算器(ALU)的操作數的字節長度一般就是int的字節長度,同時也是CPU的通用寄存器長度。
因此,即使兩個char類型相加,在CPU執行時,也要先轉換成int類型才能計算

好了,我說完了。我也把答案放出來吧
在這裏插入圖片描述
不能理解很正常,我把過程再放出來你就理解了~
在這裏插入圖片描述
這裏有個要注意的地方,整形提升是將符號位補齊哦!



好了,終於我們終於瞭解二進制運算了!
那我們可以用二進制計數法來了解位的操作

##位操作

首先先聲明一下,本Blog用的是8位二進制數,也即一個字節(Byte)
除了第一位我們剛剛介紹的
符號位*(sign - magnitude) 外,其他0~7位,我們稱爲“位” 從左到右,編號爲 7 ~ 0 其中7爲最高位,0爲最低位。*

還有要注意的是,運算過程中,統統爲補碼在工作哦,前面提到過了。

  1. 按位與操作符‘ &’
    不光是C語言,“與”我們已經很熟悉了,表達式中,只要有一方爲假(0),即整個式子就爲假(0),只有當兩方爲真(1),最後返回值才爲真(1)
    舉個例子:
    你和你女朋友出去約會:你問她想去哪?
    她說:“海邊和圖書館”
    懂我意思嗎?那肯定兩個地方都要去!




eg: (00010010) & (10011110) = 00010010
(10010011) & (00111101) = 00010001

還可以順帶掌握 “&=”
a &= 0001 等價於 a = a & 0001

  1. 按位或 ’ |’
    表達式中,只要一方爲真(1),即爲真(1)

    和上面的例子一樣
    但是這次她說:海邊或圖書館吧
    那你選擇一個你覺得更浪漫的地方去就好了~

eg:(00010010) | (10011110) = 10011110
(10010011) | (00111101) = 10111111
相同的,也有 “|=”
用法和上面"&="一樣


  1. 按位異或 ’ ^’
    表達式中,相異爲真(1),相同爲假(0)
    這個。。不太好舉實體例子,但是!聰明的你看完下面的代碼肯定也很好理解的!
    eg:(10010011) ^ (00111101) = 10101110


    還有一個 “^=”,用法見上。

4.按位取反 ’ ~‘
把補碼中1變成0,0變成1就好了
eg:~(10011010) = (01100101)

##位移操作符

*同樣的,這裏也使用二進制數

1.左移 " << "
規則:整體向左移動N位(拋棄),然後右邊補0
假設 a = 1
現在 int b = a<<1
未移動前:
在這裏插入圖片描述
移動後:
在這裏插入圖片描述
eg : int b = 5 << 1
b = 10
所以,左移其實有乘以2^n的效果









2 右移 “>>”

規則:1.算術右移( 左邊用原來的數字補齊,右邊丟棄)
2.邏輯右移 (左邊用0補齊,右邊丟棄)

這裏要說明一下,現在大部分編譯器支持的都是算術右移,所以我們這裏只講算術右移,邏輯右移如果有興趣可以下去自己試試,兩者有時結果不一樣

未移動前:
在這裏插入圖片描述
移動後:

在這裏插入圖片描述

eg: int b = 8 >> 1 = 4
所以和左移相反,右移有着除以2^n的效果

##按位異或的一些妙用

呼,終於寫到了這。
其實到了這才真正開始燒腦

首先,按位與是一個很有意思的運算方式。
首先先介紹一下按位異或的兩個小特點:
1.0與N異或 = N
即 0 ^ 2 = 2; 0 ^ 4 = 4;
2.N與N異或 = 0
即 1 ^ 1 =0; 2 ^ 2 = 0;
知道了這兩點之後,再來看接下來的內容





在這裏插入圖片描述
這是一道Leetcode上的題目
博主開始遇到這題第一想法是排序後與0~n的的數組進行比較,然後找出漏掉的那個數
但是!這裏有限制,那就沒法排序了啊,要知道博主若使用快速排序(時間複雜度爲(O(n*log(n))都不行,更何況冒泡排序(時間複雜度爲O(N^2))
所以,學到了一招騷方法



用異或來寫
思路:設置一個num = 0;
用num 去和 缺少數的數組A進行異或,再和數組B [0,N]去異或
答案就是最後異或出來的數字
原理:相同的兩個數字異或爲0,而缺少的數字n在數組B中存在,所以最後得出結果。



這樣雖說還是遍歷了一遍,但是滿足了題目要求~

class Solution {
   
   
public:
    int missingNumber(vector<int>& nums) {
   
   
        int n=nums.size();
        int ans=0;
        for(int i=1;i<n;i++){
   
   
            ans^=i^nums[i];
        }
        return ans^nums[0]^n;
    }
};

當然還有很多類似的題目,我覺得凡是需要在數組中找尋某個數字,和別的數組比較的那種
都可以用異或這一特點來破解。

The end
2021/1/29 *文章中摘取了《C premier Plus》
leetcode 作者 snowmonster的代碼.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章