第十二章 位 運(yùn) 算 12.1位運(yùn)算及其應(yīng)用 12.1.1位運(yùn)算的分類: 位邏輯運(yùn)算,、位移運(yùn)算兩種 12.1.2位邏輯運(yùn)算[符]: & | ^ ~ 【位邏輯運(yùn)算的特點(diǎn)】 ▲只作用于整型,、字符型數(shù)據(jù); ▲作用于整型,、字符型數(shù)據(jù)的每個(gè)二進(jìn)制位,,不是數(shù)的整體 ▲而一般邏輯運(yùn)算是作用數(shù)的整體,不是數(shù)的每個(gè)二進(jìn)制位,。運(yùn)算結(jié)果是二進(jìn)制數(shù),。 1、按位與運(yùn)算符(&): 1)運(yùn)算規(guī)則: 0&0=0 1&0=0 0&1=0 1&1=1 ▲只要對應(yīng)位上的值均為1則該位上的結(jié)果值為1 2)特殊作用,。 ①將一個(gè)存儲單元各位清0 ②取某個(gè)數(shù)中的某些位,。 ▲方法:將本數(shù)與某個(gè)特定數(shù)按位與運(yùn)算即可。 2,、按位或運(yùn)算符(|) 1)運(yùn)算規(guī)則: 0|0=0 1|0=1 0|1=1 1|1=1 ▲只要對應(yīng)位上的值其中一個(gè)為1則該位上的結(jié)果值為1 2)特殊作用,。 ①常用于將一個(gè)數(shù)的某些特定位置為1 ▲方法:將本數(shù)與某個(gè)特定數(shù)按位或運(yùn)算即可。 3,、按位異或運(yùn)算符(^) 1)運(yùn)算規(guī)則: 0^0=0 1^0=1 0^1=1 1^1=0 ▲只要對應(yīng)位上的值互不相同則該位上的結(jié)果值為1 2)特殊作用[p300],。 ①使某些特定的翻轉(zhuǎn) ②任何數(shù)與0相異或結(jié)果保留原數(shù)本身 ③交換兩個(gè)變量的值不用中間變量。 a=a^b b=b^a a=a^b ▲注意給變量賦值的先后順序,。 4,、按位異求反運(yùn)算符(~) 1)運(yùn)算規(guī)則: ~0=1 ~1=0 ▲對每個(gè)上的值按位求反:1變?yōu)?/span>0;0變?yōu)?/span>1,。 ▲注意以上位邏輯運(yùn)算的優(yōu)先級別,。 ▲注意:~運(yùn)算符比算術(shù)運(yùn)算、關(guān)系運(yùn)算,、邏輯運(yùn)算和其它運(yùn)算的優(yōu)先級別都高,。 12.1.3位移運(yùn)算[符]: << 和 >> 1、左位移運(yùn)算符(<<): · 1)運(yùn)算規(guī)則: a=a<<n 將a中所有位向左移動n位,。 · 2)運(yùn)算的作用:相當(dāng)于乘法運(yùn)算,。左移一位相當(dāng)于乘以2。 ▲高位[左邊位]左移后溢出被舍棄,,不起作用,。低位補(bǔ)以0。 位移運(yùn)算及其作用[右位移運(yùn)算(>>)] 1)運(yùn)算規(guī)則: a=a>>n 將a中所有位向右移動n位,。 2)運(yùn)算的作用:相當(dāng)于除法運(yùn)算,。右移一位相當(dāng)于除以2。 【注意】注意數(shù)的符號問題[即正負(fù)的問題] ▲對于無符號數(shù)[正數(shù)]右移時(shí)高位補(bǔ)以0,。 ▲對于有符號數(shù),,高位為0[正數(shù)]右移時(shí)高位補(bǔ)以0。 ▲對于有符號數(shù),,高位為1[負(fù)數(shù)]時(shí): ①右移時(shí)高位補(bǔ)以0,,稱之為“邏輯位移”。 ②右移時(shí)高位補(bǔ)以1,,稱之為“算術(shù)位移”,。 ▲TC采用“算術(shù)位移”,,高位補(bǔ)以1。 左位移運(yùn)算符(<<)
12.1.4 位運(yùn)算賦值運(yùn)算符 位運(yùn)算符與賦值運(yùn)算符可以組成復(fù)合賦值運(yùn)算符如:&=, |=, >>=, <<=, ∧= 例如,a & = b相當(dāng)于 a = a & b,。a << =2相當(dāng)于:a = a << 2,。 12.1.5 不同長度的數(shù)據(jù)進(jìn)行位運(yùn)算 如果兩個(gè)數(shù)據(jù)長度不同(例如long型和int型)進(jìn)行位運(yùn)算時(shí)(如a & b,而a為long型,b為int型),系統(tǒng)會將二者按右端對齊。如果b為正數(shù),則左側(cè)16位補(bǔ)滿0,。若b為負(fù)數(shù),左端應(yīng)補(bǔ)滿1,。如果b為無符號整數(shù)型,則左側(cè)添滿0。 12.2 位運(yùn)算舉例 例 12.1取一個(gè)整數(shù)a從右端開始的4~7位,。 可以這樣考慮: ① 先使a右移4位,。見圖12.3。圖12.3(a)是未右移時(shí)的情況,(b)圖是右移4位后的情況,。目的是使要 取出的那幾位移到最右端,。
右移到右端可以用下面方法實(shí)現(xiàn): a >> 4 ② 設(shè)置一個(gè)低4位全為1,其余全為0的數(shù)??捎孟旅娣椒▽?shí)現(xiàn): ~ ( ~ 0 << 4 ) ~0的全部二進(jìn)制為全1,,左移4位,這樣右端低4位為0,。見下面所示: 0:0000…000000 ~0: 1111…111111 ~0<<4:1111…110000 ~(~0<<4):0000…001111 ③ 將上面二者進(jìn)行&運(yùn)算,。即 (a >> 4) & ~ ( ~ 0 << 4 ) 根據(jù)上一節(jié)介紹的方法,與低4位為1的數(shù)進(jìn)行&運(yùn)算,,就能將這4位保留下來,。 程序如下: main() { unsigned a,b,,c,,d; scanf("%o",,&a),; b=a>>4; c=~(~0<<4),; d=b&c,; printf("%o, %d\n%o,, %d\n",,a,a,,d,,d); } 運(yùn)行情況如下: 331 331,, 217(a的值) 15,, 13 (d的值) 輸入a的值為八進(jìn)制數(shù)331,, 即十進(jìn)制數(shù)217, 其二進(jìn)制形式為11011001,。經(jīng)運(yùn)算最后得到的d為00001101,,即八進(jìn)制數(shù)15,十進(jìn)制數(shù)13,。
可以任意指定從右面第m位開始取其右面n位。只需將程序中的“b=a>>4”改成“b=a>>(m-n+1)”以及將“c=~(~0<<4)”改成“c=~(~0<<n)”即可,。 例12.2循環(huán)移位,。要求將a進(jìn)行右循環(huán)移位。見圖12.4,。圖12.4表示將a右循環(huán)移n位,。即將a中原來左面(16-n)位右移n位,原來右端n位移到最左面n位,。今假設(shè)用兩個(gè)字節(jié)存放一個(gè)整數(shù),。為實(shí)現(xiàn)以上目的可以用以下步驟: ① 將a的右端n位先放到b中的高n位中??梢杂孟旅嬲Z句實(shí)現(xiàn):b=a<<(16-n),; ② 將a右移n位,其左面高位n位補(bǔ)0,??梢杂孟旅嬲Z句實(shí)現(xiàn): c=a>>n; ③ 將c與b進(jìn)行按位或運(yùn)算,。即 c=c|b,; 程序如下: main( ) { unsigned a,b,,c,; int n; scanf("a=%o,,n=%d",,&a,&n),; b=a<<(16-n),; c=a>>n; c=c|b,; printf("%o\n%o",,a,c),; } 運(yùn)行情況如下: a=157653,,n=3 0 157653 75765 運(yùn)行開始時(shí)輸入八進(jìn)制數(shù)157653,,即二進(jìn)制數(shù)1101111110101011,循環(huán)右移3位后得二進(jìn)制數(shù)0111101111110101,,即八進(jìn)制數(shù)75765,。同樣可以左循環(huán)位移。 12.3 位段 以前曾介紹過對內(nèi)存中信息的存取一般以字節(jié)為單位,。實(shí)際上,,有時(shí)存儲一個(gè)信息不必用一個(gè)或多個(gè)字節(jié),例如,,“真”或“假”用0或1表示,,只需1位即可。在計(jì)算機(jī)用于過程控制,、參數(shù)檢測或數(shù)據(jù)通信領(lǐng)域時(shí),,控制信息往往只占一個(gè)字節(jié)中的一個(gè)或幾個(gè)二進(jìn)位,常常在一個(gè)字節(jié)中放幾個(gè)信息,。那么,,怎樣向一個(gè)字節(jié)中的一個(gè)或幾個(gè)二進(jìn)位賦值和改變它的值呢?可以用以下兩種方法: (1) 可以人為地在一個(gè)字節(jié)data中設(shè)幾項(xiàng),。例如:a,、b、c,、d分別占2位,、6位、4位,、4位(見圖12.5),。如果想將c的值變?yōu)?/span>12(設(shè)c原來為0),可以這樣:
圖12.5 ① 將數(shù)12左移4位,,使1100成為右面起第4~7位,。 ② 將data與“12<<4” 進(jìn)行“按位或” 運(yùn)算,即可使c的值變成12,。 如果c的原值不為0,,應(yīng)先使之為0??梢杂孟旅娣椒ǎ?/span> data=data & 0177417 0177417的二進(jìn)制表示為 11 11111 1 0000 1111 a b c d 也就是使第4~7位全為0,其他位全為1,。它與data進(jìn)行 &運(yùn)算,使第4~7位為0,其余各位保留data的原狀。這個(gè)177417稱為“屏蔽字”,即把c以外的信息屏蔽起來,不受影響,只使c改變?yōu)?/span>0,。 但要找出和記住177417這個(gè)數(shù)比較麻煩,。可以用data=data & ~ ( 15 << 4 );15是c的最大值,c共占4位,最大值為1111即15。15<<4是將1111移到4~7位,。 再取反,就使4~7位變成0,其余位全是1,。即 15:0000000000001111 15 << 4:0000000011110000 ~ ( 15 << 4 ):1111111100001111 這樣可以實(shí)現(xiàn)對c清0,而不必計(jì)算屏蔽碼。 將上面幾步結(jié)合起來,可以得到 data=data & ~ ( 15 << 4 )| ( n & 15 ) << 4; 賦予4~7位為0 n為應(yīng)賦給c的值(例如12)\. n & 15的作用是只取n的右端4位的值,其余各位置0,即把n放到最后4位上,( n & 15 ) << 4, 就是將n置在4~7位上,。見下面: data & ~(15<<4): 11011011|0000|1010 (n & 15)<<4: 00000000|1100|0000 (按位或運(yùn)算) 11011011|1100|1010 可見,data的其他位保留原狀未改變,而第4~7位改變?yōu)?/span>12(即1100)了,。 但是用以上辦法給一個(gè)字節(jié)中某幾位賦值太麻煩了??梢杂孟旅娼榻B的位段結(jié)構(gòu)體的方法,。 (2) 位段 C語言允許在一個(gè)結(jié)構(gòu)體中以位為單位來指定其成員所占內(nèi)存長度,這種以位為單位的成員稱為“位段”或稱“位域” ( bit field) ,。利用位段能夠用較少的位數(shù)存儲數(shù)據(jù),。 例如: struct packed-data { unsigned a∶2; unsigned b∶6,; unsigned c∶4; unsigned d∶4,; int i,; }data; 見圖12.6,。其中a,、b、c,、d分別占2位,、6位、4位,、4位,。i為整型。共占4個(gè)字節(jié),。
圖12.6 也可以使各個(gè)位段不恰好占滿一個(gè)字節(jié),。如: struct packed-data { unsigned a∶2; unsigned b∶3,; unsigned c∶4,; int i; },; struct packed-data data,; 見圖12.7。其中a,、b,、c共占9位,占1個(gè)字節(jié)多,不到2個(gè)字節(jié),。它的后面為int型,,占2個(gè)字節(jié)。在a,、b,、c之后7位空間閑置不用,i從另一字節(jié)開頭起存放,。
圖12.7
圖12.8 注意,,在存儲單元中位段的空間分配方向,因機(jī)器而異,。在微機(jī)使用的C系統(tǒng)中,,一般是由右到左進(jìn)行分配的,如圖12.8,。但用戶可以不必過問這種細(xì)節(jié),。對位段中的數(shù)據(jù)引用的方法。如: data.a=2,; data.b=7,; data.c=9; 注意位段允許的最大值范圍,。如果寫data.a=8,; 就錯了。因?yàn)?/span>data.a只占2位,,最大值為3,。在此情況下,自動取賦予它的數(shù)的低位,。例如,,8的二進(jìn)制數(shù)形式為1000,而data.a只有2位,,取1000的低2位,,故data.a得值0。 關(guān)于位段的定義和引用,,有幾點(diǎn)要說明: (1) 位段成員的類型必須指定為unsigned或int類型,。 (2) 若某一位段要從另一個(gè)字開始存放??梢杂靡韵滦问蕉x: unsigned a∶1,; unsigned b∶2; 一個(gè)存儲單元 unsigned∶0,; unsigned c∶3,; (另一存儲單元) 本來a,、b、c應(yīng)連續(xù)存放在一個(gè)存儲單元(字)中,,由于用了長度為0的位段,,其作用是使下一個(gè)位段從下一個(gè)存儲單元開始存放。因此,,現(xiàn)在只將a,、b存儲在一個(gè)存儲單元中,c另存放在下一個(gè)單元,。(上述“存儲單元”可能是一個(gè)字節(jié),,可能是29字節(jié),視不同的編譯系統(tǒng)而異,。) (3) 一個(gè)位段必須存儲在同一存儲單元中,,不能跨兩個(gè)單元。如果第一個(gè)單元空間不能容納下一個(gè)位段,,則該空間不用,,而從下一個(gè)單元起存放該位段。(4) 可以定義無名字段,。如: unsigned a∶1,; unsigned ∶2;(這兩位空間不用) unsigned b∶3,; unsigned c∶4;
見圖12.9,。在a后面的是無名位段,,該空間不用。
圖12.9 (5) 位段的長度不能大于存儲單元的長度,,也不能定義位段數(shù)組,。 (6) 位段可以用整型格式符輸出。如: printf("%d,,%d,,%d",data.a,,data.b,,data.c); 當(dāng)然,,也可以用%u,、%o、%x等格式符輸出,。 (7) 位段可以在數(shù)值表達(dá)式中引用,,它會被系統(tǒng)自動地轉(zhuǎn)換成整型數(shù)。如:data.a+5/data.b是合法的。
|
|