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