C语言再学习 -- 位操作
一、二進制
二進制是計算技術中廣泛采用的一種數制。二進制數據是用0和1兩個數碼來表示的數。它的基數為2,進位規則是“逢二進一”,借位規則是“借一當二”,由18世紀德國數理哲學大師萊布尼茲發現。
位:"位(bit)"是電子計算機中最小的數據單位。每一位的狀態只能是0或1。
字節:8個二進制位構成1個"字節(Byte)",它是存儲空間的基本計量單位。1個字節可以儲存1個英文字母或者半個漢字,換句話說,1個漢字占據2個字節的存儲空間。
字:"字"由若干個字節構成,字的位數叫做字長,不同檔次的機器有不同的字長。例如一臺8位機,它的1個字就等于1個字節,字長為8位。如果是一臺16位機,那么,它的1個字就由2個字節構成,字長為16位。字是計算機進行數據處理和運算的單位。?二進制沒有占位符
二進制1011表示:
1x2^3 + 0x2^2 + 1x2^1 + 1x2^0 = 11
-5 ?二進制為 1111 1011 ? ? ? ? ? ?十進制數之間轉二進制 關系是 ?取反加一
5的 ?二進制 ? 0000 0101
取反 ? ? ? ? ?1111 1010
加一 ? ? ? ? ?1111 1011
有符號整數參看:C語言再學習 -- 負數
二進制浮點數參看:C語言再學習 -- 浮點數
二、八進制
把二進制從右向左每三個數位分成一組,每一組單獨轉換成十進制結果一定在0到7之間。把所有組的轉換結果按順序書寫就得到數字的八進制表示方式。
例如,八進制數451(在C中寫為0451)表示:
4x8^2 + 5x8^1 + 1x8^0 = 297
可以在程序中用八進制方式表示數字必須以0作為開頭,采用%o作為占位符可以把一個整數的八進制表示方式打印在屏幕上
三、十六進制
把二進制數字從右向左每四個數位分成一組,每組單獨轉換成十進制一定在0到15之間,如果轉換結果在10到15之間用英文字母a到f分別表示,把所有轉換結果按順序書寫就得到數字的十六進制表示方式
例 0110 1010 轉換成十六進制為 6a ?
例 0010 1011 ?43 ?053 ? 2b
在程序中使用十六進制方式表示數字,必須以0x開頭,采用%x或者%X作為占位符可以把數字的十六進制表示方式打印在屏幕,上打印結果中不包含0x。
%x做占位符的時候打印結果中的英文字母都是小寫的
%X做占位符的時候打印結果中的英文字母都是大寫的
//占位符 #include <stdio.h> int main() {printf("0%o %d\n",0152,0152); //八進制 記得前面加個 0printf("0x%X 0x%x 0%o %d\n",0xcb,0xcb,0xcb,0xcb); // 十六進制記得前面加 0xreturn 0; } 輸出結果: 0152 106 0xCB 0xcb 0313 203
參看:C語言再學習 -- printf、scanf占位符
參看:二進制轉換對照表
擴展:進制轉換器
1、整數部分的二進制轉換成十進制。
| 指數 | 十進制數 | 二進制數 |
| 20 | 1 | 0001 |
| 21 | 2 | 0010 |
| 22 | 4 | 0100 |
| 23 | 8 | 1000 |
| 24 | 16 | 0001 0000 |
| 25 | 32 | 0010 0000 |
| 26 | 64 | 0100 0000 |
| 27 | 128 | 1000 0000 |
| 28 | 256 | 0001 0000 0000 |
| 29 | 512 | 0010 0000 0000 |
| 210 | 1024 | 0100 0000 0000 |
| 211 | 2048 | 1000 0000 0000 |
| 212 | 4096 | 0001 0000 0000 0000 |
| 213 | 8192 | 0010 0000 0000 0000 |
| 214 | 16384 | 0100 0000 0000 0000 |
| 215 | 32768 | 1000 0000 0000 0000 |
| 216 | 65536 | 0001 0000 0000 0000 0000 |
2、小數部分的二進制轉換成十進制。
記到小數點后六位就夠了,如果再向后,你可以繼續除2,不過這題目可就有些變態了。
| 指數 | ?分數 | 二進制 | 十進制 |
| ?2-1 | ?1/21 | ?.1 | ?.5 |
| ?2-2 | ?1/22 | ?.01 | ?.25 |
| ?2-3 | ?1/23 | ?.001 | ?.125 |
| ?2-4 | ?1/24 | ?.0001 | ?.0625 |
| ??2-5 | ?1/25 | ?.0000 1 | ?.03125 |
| ??2-6 | ?1/26 | ?.0000 01 | ?.015625 |
3、二進制(B,Binary),八進制(O,Octal) 十進制(D,Decimalist),十六進制(H,Hex)
| 二進制 | 八進制 | 十進制 | 十六進制 |
| 0000 | 0 | 0 | 0 |
| 0001 | 1 | 1 | 1 |
| 0010 | 2 | 2 | 2 |
| 0011 | 3 | 3 | 3 |
| 0100 | 4 | 4 | 4 |
| 0101 | 5 | 5 | 5 |
| 0110 | 6 | 6 | 6 |
| 0111 | 7 | 7 | 7 |
| 1000 | 10 | 8 | 8 |
| 1001 | 11 | 9 | 9 |
| 1010 | 12 | 10 | A |
| 1011 | 13 | 11 | B |
| 1100 | 14 | 12 | C |
| 1101 | 15 | 13 | D |
| 1110 | 16 | 14 | E |
| 1111 | 17 | 15 | F |
四、位運算符
1、二進制反碼或按位取反:~
需要確認該值是否為unsigned類型,如果是有符號則,正數為原來的數取反、加1
例如:~6?
0000 0110
1111 1001 =473 = -0000 0111 ==-7
2、&(按位與)(串聯)
只有對應數位上都是1的時候結果才是1
3 二進制 ? ? ? 0000 0011 ? ?
5 二進制 ? ? ? 0000 0101
按位與 ?& 為 0000 0001
結果是 1
C也有一個組合的位與賦值運算符:&=。下面兩個語句產生相同的最后結果:
val &= 0377; val = val & 0377
3、|(按位或)(并聯)
只要對應數位中有1則結果就是1
3 二進制 ? ? ?0000 0011 ??
5 二進制 ? ? ?0000 0101
按位或 ?| ?為 0000 0111
結果為 7
C也有一個組合的位或賦值運算符:|=。下面兩個語句產生相同的最后結果:
val 1= 0377; val = val | 0377
4、^(按位異或)
如果對應數位內容一樣則結果是0,否則結果為1
3 二進制 ? ? ? 0000 0011 ??
5 二進制 ? ? ? 0000 0101
按異或 ?^ 為 0000 0110
結果位 6
C也有一個組合的位異或賦值運算符:^=。下面兩個語句產生相同的最后結果:
val ^= 0377; val = val ^0377
5、移位運算符
移動操作符可以把數字中每個二進制數位統一想左或者向右移動n個位置,移動操作會得到一個新數字,不會修改原來的數字。
1) <<表示向左移動操作
向左移動是右邊空出來的位置上一定補充0
例如 二進制 ?(0000 0011) << 2 ?向左移動兩位結果為 ?0000 1100
3 ? ?0000 0011 ? ? 3 x 2^2 =12
? ? ? 0000 1100
再如:
-5 << 2 = -20
也可以這么理解,向左移動n位相當于乘以2的n次方。
2) >>表示向右移動操作
對于unsigned類型,右移時使用0填充左端空出的位。對于有符號類型,結果依賴于機器。空出的位可能用0填充,或者使用符號(最左端的)位的副本填充。
例如 二進制 (0000 1100) >> 2 向右移動兩位結果為 0000 0011
12 ? ?0000 1100 ? ? ? 12 / 2^2 = 3
? ? ? ? ?0000 0011
也可以這么理解,相對于unsigned類型而言,向右移動n位相當于除以2的n次方
注意:
應避免使用 a << -5 這種類型的移位,因為它們的效果是不可預測的,使用類型移位的程序時不可移植的。
編譯器會出現,警告: 左移次數為負 [默認啟用]。
再來看看下面的例子:
0x01 << 2 + 3; 結果是多少?
#include <stdio.h>int main (void) {printf ("%d\n", 0x01 << 2 + 3);return 0; } 輸出結果: 32 因為?"+" 號的優先級比移位運算符的優先級高。
如果再把例子改寫一下:
0x01 << 2 + 30; ?或者 0x01 << 2 -3; ?這樣行嗎?
#include <stdio.h>int main (void) {printf ("%d\n", 0x01 << 2 + 30);return 0; } 輸出結果: 警告: 左移次數大于或等于類型寬度 [默認啟用] #include <stdio.h>int main (void) {printf ("%d\n", 0x01 << 2 - 3);return 0; } 輸出結果: 警告: 左移次數為負 [默認啟用] 首先考慮的還是運算符優先級,然后看,一個整型數長度為 32 位,左移 32 位則會溢出。左移 -1 位也是不對的。
所以說,左移和右移的位數不能大于數據的長度,不能小于 0。
五、位運算符用法
1、掩碼
flags &= MASK;
例如:flags二進制為 1001 0110 ? MASK二進制為 0000 0010 即:
flags &= 0x02;
flags = 0000 0010
這個語句將導致flags的除位 1之外,所有位都被設為 0。
2、打開位
flags |= MASK;
例如:flags二進制為 1001 0100 ? MASK二進制為 0000 0010 即:
flags |= 0x02;
flags = 1001 0110
這個語句將flags中的位1設為1,并保留其他所有位不變。
結合移位運算符
flags |= MASK << n; ? ? ??
例如:flags二進制為 1001 0110 ? MASK二進制為 0000 0001 ?n為4即:
flags |= 0x01<<4; ? ?(高電平)
flags = 1001 1110
這個語句將flags的位 3 設為1,并保留其他所有位不變。
3、關閉位
flags &= ~MASK;
例如:flags二進制為 1001 0110 ? MASK二進制為 0000 0010 即:
flags &= ~0x02;
flags = 1001 0100
這個語句將flags除位1設為0以外,保留其他所有位不變。
結合移位運算符
flags &= ~(MASK << n); ?
例如:flags二進制為 1001 0110 ? MASK二進制為 0000 0001 ?n為3即:
flags &= ~(0x01<<3); ? ?(低電平)
flags = 1001 0010
這個語句將flags的位 2 設為0,并保留其他所有位不變 ? ?
4、轉置位
flags ^= MASK;
例如:flags二進制為 1001 0110 ? MASK二進制為 0000 0010 即:
flags ^= 0x02;
flags = 1001 0100
轉置一個位表示如果該位打開,則關閉該位,如果該位關閉,則打開該位。
5、查看一位的值
if ((flags & MASK) == MASK)
puts ("Wow);
例如:flags二進制為 1001 0110 ? MASK二進制為 0000 0010 即:
if ((flags & 0x02) == 0x02)
puts ("Wow);
這個語句可判斷flags位1是否為1,由于位運算符的優先級低于==,因此需要在flags & MASK的兩側加上圓括號。
六、位字段
擴展:C語言中的位字段
總結
以上是生活随笔為你收集整理的C语言再学习 -- 位操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 互联网晚报 | 3月22日 星期二 |
- 下一篇: 前后端分离Java后端跨越问题解决