C語言各種符號的使用介紹下篇
1、按位運算符
1.1 按位或( | )和按位與( & )
上期我們講到過邏輯或和邏輯與,他們得到的結果是真假值,但我們一定要區分清楚,按位運算符 "|" 和 "&" 與邏輯運算符 "||" "&&" 是完全兩個概念。
按位,簡明之意,按數值二進制位來進行運算,都是在數據補碼的基礎上進行。
按位或 "|" :兩個數值的二進制補碼對應位進行運算,對應位有 1 則為 1 ,否則為 0。
按位與 "&":兩個數值的二進制補碼對應位進行運算,對應位都為 1 則為 1, 否則為 0。
這裡我們舉例說明:
1 | 2 :
1 的二進制補碼:0000 0000 … 0000 0001
2 的二進制補碼:0000 0000 … 0000 0010
——按位或結果: 0000 0000… 0000 0011 -> 對應十進制:3
1 & 2:
1 的二進制補碼:0000 0000 … 0000 0001
2 的二進制補碼:0000 0000 … 0000 0010
——按位與結果: 0000 0000… 0000 0000 -> 對應十進制:0
其實有很多大學老師或者是書上都有可能把按位或,按位與,以及後面我們要講的按位異或,他們會把每位二進制運算後的結果稱為真或者假,其實這樣的說法是不夠嚴謹的,真假是邏輯判斷,而按位運算得到的結果是數值,而且在C語言中0表示假,非0為真,所以我是不推薦這種說法。
1.2 按位異或( ^ )
按位或 "^" :兩個數值的二進制補碼對應位進行運算,相同為 0 , 不同為 1。
這裡我們舉例說明:
1 ^3:
1 的二進制補碼:0000 0000 … 0000 0001
3 的二進制補碼:0000 0000 … 0000 0011
—按位異或結果: 0000 0000… 0000 0010 -> 對應十進制:2
5 ^0:
5 的二進制補碼:0000 0000 … 0000 0101
0 的二進制補碼:0000 0000 … 0000 0000
—按位異或結果: 0000 0000… 0000 0101 -> 對應十進制:5
結論:任何數異或0都等於它本身
這裡有一道筆試題:不創建臨時變量,實現兩個數的交換。
//很多小夥伴直接想出來的做法: int main() { int a = 10; int b = 20; printf("a = %d, b = %d\n", a, b); a = a + b; b = a - b; a = a - b; printf("a = %d, b = %d\n", a, b); return 0; }
但是我們仔細研究下這段代碼,他有沒有什麼隱藏的問題呢?
一個整型,占四個字節,也就是 32 個比特位,這裡進行加法運算,就會產生進位,萬一我們是兩個很大的數相加呢?他們的和超過瞭整型最大存儲范圍,那麼在計算機裡面就會發生截斷!為瞭避免發生這種現象,我們可以采取異或的方法來實現這道題:
最後還有一個很簡單的按位取反操作符:~
用途:對一個數的二進制按位取反(包括它的符號位)
註意:以上的位運算符,他們的操作數必須是整數!
1.3 一個關於整型提升的問題
有這樣一串代碼,問:為什麼一個char類型大小可以求出來是4字節?
無論任何位運算符,都是要計算機進行計算的,而計算機中CPU具有運算能力,但計算的數據都是放在內存中的。所以,做任何運算,都必須將數據從內存拿到CPU的寄存器中。而寄存器默認的操作數寬度是32位,可是,char類型數據隻有1個字節,也就是8位,不滿足32位怎麼辦,這就需要整型提升瞭!(詳細整型提升大傢可以查閱資料哦)
如果是一個有符號數的話:高位補符號位
如果是一個無符號數的話:高位補0
2、移位操作符
2.1 左移<< 右移>>操作符
<< 左移運算符是一個雙目運算符,功能是把左邊的運算數的各個二進制位向左移動指定位數。
>> 右移運算符是一個雙目運算符,功能是把右邊的運算數的各個二進制位向右移動指定位數。
註意:
<< 左移:最低位丟棄,最高位補零
>> 右移:
- 無符號數:最低位丟棄,最高位補零 [邏輯右移]
- 有符號數:最低位丟棄,最高位補符號位 [算數右移]
以上在補碼中進行運算
警告:移位運算符,請不要移動負數位,這是標準未定義的!
左移我們好說,主要是右移我們需要細講一下:
明顯看到,這是在無符號數下進行右移,第一個小夥伴都不會感到驚訝, 可是第二個就有點不理解瞭,我們來解釋下:
這裡有一個問題,當 -1 準備放入變量 b 的時候我們需要看-1的類型嗎?
答案是不需要!內存中放的都是二進制補碼,本質上是把 -1 的補碼放入變量 b 當中,第二,右移操作符屬於計算,需要在CPU中進行,所以需要先把內存中 -1 的補碼拿到CPU寄存器中運算,按照我們的規則,右移中,無符號數低位丟棄高位補零,所以 -1 右移完成之後就變成瞭 0111 1111 … 1111 1111,接著我們以 %d 有符號整型打印,就會把他當作有符號數看待,最高位是 0 所以被認為是正數,轉化成十進制也就是如上打印的值。
第二個我們來看下有符號數右移:
這個相信大傢就很好理解瞭,第一個高位補符號位也就是補 0,低位丟棄,所以結果是 0,第二個高位補符號位也就是補 1,低位丟棄,值仍然不變,還是 -1。
註意:a>>1 並不會改變 a 變量的值,就好比如 a + 1。這樣寫才會改變:a = a >> 1;
2.2 習題練習
學完瞭上期的邏輯操作符,和本期的移位操作符,我們來練練手:
請你設計一個宏可以指定數據第幾個比特位更改為 1 ,並設計一個函數將各個比特位打印出來。
//參考 #define SETBIT(a, num) ((a) |= (1 << (num - 1)) ) void PrintBit(int a) { int num = 31; while (num >= 0) { if ((a & (1 << num))) printf("1"); else printf("0"); --num; } printf("\n"); } int main() { int a = 0; SETBIT(a, 5); PrintBit(a); return 0; }
3、++和–的操作
3.1 基本操作
其實這節知識點理解起來是很簡單的,隻不過總有些學校喜歡出一些很拉跨的題目:
int i = 3; 問:(++i) + (++i) + (++i) 的值是多少?
我的建議是,看到這類題,直接空著,你也可以在下面添一句,“ 你禮貌嗎?”
這種表達式,在任何編譯器下算出來的結果是不一樣的!
對於這種問題沒必要去爭論誰對誰錯, 如果有人想跟你杠的話,那麼你直接告訴他,你真的超級高水平。
好瞭,言歸正傳,我們來說一下 ++ 和 — 的基本理解:
- 前置++ — :先自增(減),再使用
- 後置++ — : 先使用,再自增(減) 如果沒有變量接收,那麼直接自增。
例子:
基本使用就是這麼多,接下來我們從匯編角度來深度理解一下:
3.2 從匯編角度深入理解a++
既然我們知道,後置++ 是先使用後++,如果我們單純的就 ++ 一下呢,他這個值被使用到瞭哪裡去瞭呢?
int main() { int a = 0xDD; int b = a++; //有b接收,那麼a的先使用是將a的值(內容),放到b中 int c = 0xEE; c++; //沒有接收方,那麼"先使用",如何理解? return 0; }
vs2019編譯器反匯編:
結論:後置++ 完整的含義是先使用,在自增,如果沒有變量接收,那麼直接自增。
註意:在不同的編譯器可能處理過程不同,不過這是一個基本的研究過程,比單純的理論學習更嚴謹。
到此這篇關於C語言各種符號的使用介紹下篇的文章就介紹到這瞭,更多相關C語言符號內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!