基本信息
移位運算符的一種:
C 語言中移位運算符
位移位運算符
位移位運算符是將數據看成二進制數,對其進行向左或向右移動若干位的運算。位移位運算符分為左移和右移兩種,均為雙目運算符。第一運算對象是移位對象,第二個運算對象是所移的二進制位數。
位移位運算符的運算對象、運算規則與結果、結合性如表2-16所示。
移位時,移出的位數全部丟棄,移出的空位補入的數與左移還是右移有關。如果是左移,則規定補入的數全部是0;如果是右移,還與被移位的數據是否帶符號有關。若是不帶符號數,則補入的數全部為0;若是帶符號數,則補入的數全部等於原數的最左端位上的原數(即原符號位)。具體移位規則如下所示。
位移位運算符的優先權如下:
·算術運算符 優先於 位移位運算符優先於關係運算符
·位移位運算符是同級別的,結合性是自左向右
例如,設無符號短整型變數a為0111(對應二進制數為0000000100010001),
則:a<<3 結果為0888(對應二進制數為0000100010001000),a不變(即運算式本身為一個值,但不改變被操作對象值)
a>>4 結果為006 (對應二進制數為0000000000010001),a不變
又如,設短整型變數a為-4(對應二進制數為1111111111111100),
則:a<<3 結果為-32(對應二進制數為1111111111100000),a不變
a>>4 結果為-1(對應二進制數為1111111111111111),a不變
C語言裡的左移和右移運算符
先說左移,左移就是把一個數的所有位都向左移動若干位,在C中用<<運算符.例如:
int i = 1;
i = i << 2; //把i里的值左移2位
也就是說,1的2進制是000...0001(這裡1前面0的個數和int的位數有關,32位機器,gcc里有31個0),左移2位之後變成000... 0100,也就是10進制的4,所以說左移1位相當於乘以2,那么左移n位就是乘以2的n次方了(有符號數不完全適用,因為左移有可能導致符號變化,下面解釋原因)
需要注意的一個問題是int類型最左端的符號位和移位移出去的情況.我們知道,int是有符號的整形數,最左端的1位是符號位,即0正1負,那么移位的時候就會出現溢出,例如:
int i = 0x40000000; //16進制的40000000,為2進制的01000000...0000
i = i << 1;
那么,i在左移1位之後就會變成0x80000000,也就是2進制的100000...0000,符號位被置1,其他位全是0,變成了int類型所能表示的最小值,32位的int這個值是-2147483648,溢出.如果再接著把i左移1位會出現什麼情況呢?在C語言中採用了丟棄最高位的處理方法,丟棄了1之後,i的值變成了0.
左移里一個比較特殊的情況是當左移的位數超過該數值類型的最大位數時,編譯器會用左移的位數去模類型的最大位數,然後按餘數進行移位,如:
int i = 1, j = 0x80000000; //設int為32位
i = i << 33; // 33 % 32 = 1 左移1位,i變成2
j = j << 33; // 33 % 32 = 1 左移1位,j變成0,最高位被丟棄
在用gcc編譯這段程式的時候編譯器會給出一個warning,說左移位數>=類型長度.那么實際上i,j移動的就是1位,也就是33%32後的餘數.在gcc下是這個規則,別的編譯器是不是都一樣現在還不清楚.
總之左移就是: 丟棄最高位,0補最低位
再說右移,明白了左移的道理,那么右移就比較好理解了.
右移的概念和左移相反,就是往右邊挪動若干位,運算符是>>.
右移對符號位的處理和左移不同,對於有符號整數來說,比如int類型,右移會保持符號位不變,例如:
int i = 0x80000000;
i = i >> 1; //i的值不會變成0x40000000,而會變成0xc0000000
就是說,符號位向右移動後,正數的話補0,負數補1,也就是彙編語言中的算術右移.同樣當移動的位數超過類型的長度時,會取餘數,然後移動餘數個位.
負數10100110 >>5(假設字長為8位),則得到的是 11111101
總之,在C中,左移是邏輯/算術左移(兩者完全相同),右移是算術右移,會保持符號位不變.實際套用中可以根據情況用左/右移做快速的乘/除運算,這樣會比循環效率高很多.
在很多系統程式中常要求在位(bit)一級進行運算或處理。C語言提供了位運算的功能,這使得C語言也能像彙編語言一樣用來編寫系統程式。
作用
& 位邏輯與
| 位邏輯或
^ 位邏輯異或
- 位邏輯反
>> 右移
<< 左移
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
按位運算是對位元組或字中的實際位進行檢測、設定或移位, 它只適用於字元型和整數型變數以及它們的變體, 對其它數據類型不適用。
我們要注意區分位運算和邏輯運算。
1. 按位與運算按位與運算符"&"是雙目運算符。其功能是參與運算的兩數各對應的二進位相與。只有對應的兩個二進位均為1時,結果位才為1 ,否則為0。參與運算的數以補碼方式出現。
例如:9&5可寫算式如下: 00001001 (9的二進制補碼)&00000101 (5的二進制補碼) 00000001 (1的二進制補碼)可見9&5=1。
按位與運算通常用來對某些位清0或保留某些位。例如把a 的高八位清 0 ,保留低八位,可作 a&255 運算 ( 255 的二進制數為0000000011111111)。
main(){
int a=9,b=5,c;
c=a&b;
printf("a=%d\nb=%d\nc=%d\n",a,b,c);
}
2. 按位或運算按位或運算符“|”是雙目運算符。其功能是參與運算的兩數各對應的二進位相或。只要對應的二個二進位有一個為1時,結果位就為1。參與運算的兩個數均以補碼出現。
例如:9|5可寫算式如下: 00001001|00000101
00001101 (十進制為13)可見9|5=13
main(){
int a=9,b=5,c;
c=a|b;
printf("a=%d\nb=%d\nc=%d\n",a,b,c);
}
3. 按位異或運算按位異或運算符“^”是雙目運算符。其功能是參與運算的兩數各對應的二進位相異或,當兩對應的二進位相異時,結果為1。參與運算數仍以補碼出現,例如9^5可寫成算式如下: 00001001^00000101 00001100 (十進制為12)
main(){
int a=9;
a=a^15;
printf("a=%d\n",a);
}
4. 求反運算求反運算符~為單目運算符,具有右結合性。其功能是對參與運算的數的各二進位按位求反。例如~9的運算為: ~(0000000000001001)結果為:1111111111110110
5. 左移運算左移運算符“<<”是雙目運算符。其功能把“<< ”左邊的運算數的各二進位全部左移若干位,由“<<”右邊的數指定移動的位數,高位丟棄,低位補0。例如: a<<4 指把a的各二進位向左移動4位。如a=00000011(十進制3),左移4位後為00110000(十進制48)。
6. 右移運算右移運算符“>>”是雙目運算符。其功能是把“>> ”左邊的運算數的各二進位全部右移若干位,“>>”右邊的數指定移動的位數。例如:設 a=15,a>>2 表示把000001111右移為00000011(十進制3)。應該說明的是,對於有符號數,在右移時,符號位將隨同移動。當為正數時,最高位補0,而為負數時,符號位為1,最高位是補0或是補1 取決於編譯系統的規定。
main(){
unsigned a,b;
printf("input a number: ");
scanf("%d",&a);
b=a>>5;
b=b&15;
printf("a=%d\tb=%d\n",a,b);
}
請再看一例!
main(){
char a="a",b="b";
int p,c,d;
p=a;
p=(p<<8)|b;
d=p&0xff;
c=(p&0xff00)>>8;
printf("a=%d\nb=%d\nc=%d\nd=%d\n",a,b,c,d);
}
當進行按位與或時,最好使用16進制,在程式中這樣表示:0x01 表示0000 0001
所以,字元類型a的最高位強制1可以這樣:a=a|0x80。其他的可以依次類推!
巧用
因為位運算比/運算快,所以可對以下代碼進行最佳化
最佳化
還可利用右移運算符計算a的第b個二進制位是什麼