一、計算機中的二進制位運算
二進制的位運算並不是很難掌握,因為位運算總共只有5種運算:與、或、異或、左移、右移。與、或和異或運算的規律我們可以用表1總結如下。
與(&) | 0 & 0 = 0 | 1 & 0 = 0 | 0 & 1 = 0 | 1 & 1 = 1 |
---|---|---|---|---|
或(|) | 0 | 0 = 0 | 1 | 0 = 1 | 0 | 1 = 1 | 1 | 1 = 1 |
異或(^) | 0 ^ 0 = 0 | 1 ^ 0 = 1 | 0 ^ 1 = 1 | 1 ^ 1 = 0 |
左移運算符m<<n表示把m左移n位。在左移n位的時候,最左邊的n位將被丟棄,同時在最右邊補上n個0。比如:
- 00001010 << 2 = 00101000
- 10001010 << 3 = 01010000
右移運算符m>>n表示把m右移n位。在右移n位的時候,最右邊的n位將被丟棄。但右移時處理最左邊的情形要稍微復雜些。如果數字是一個無符號數值,則用0填補最左邊的n位;如果數字是一個有符號數值,則用數字的符號位填補最左邊的n位。也就是說,如果數字是正數,則右移之后在最左邊補n個0;如果數字是負數,則右移之后在最左邊補n個1。下面是對8位有符號數進行右移的例子:
- 00001010 >> 2 = 00000010
- 10001010 >> 3 = 11110001
二、unsigned與signed的區別
首先回顧一下二進制的正負數表達方式。在計算機中使用補碼表示正負數,其中正數的補碼等於其本身,負數的補碼則為原碼取反再加1。用4位二進制表示-1 ~ 7如表2所示。
補碼 | 十進制數值 |
---|---|
0000 | 0 |
0001 | 1 |
0010 | 2 |
0011 | 3 |
0100 | 4 |
0101 | 5 |
0110 | 6 |
0111 | 7 |
1000 | -8 |
1001 | -7 |
1010 | -6 |
1011 | -5 |
1100 | -4 |
1101 | -3 |
1110 | -2 |
1111 | -1 |
由表2可知,在32位的系統中,int型的-1在計算機中的存儲的補碼為0xFFFF FFFF。
如同int a;一樣,int 也能被其它的修飾符修飾。除void類型外,基本數據類型之前都可以加各種類型修飾符,類型修飾符有如下四種:
- signed----有符號,可修飾char、int(Int是默認有符號的)
- unsigned-----無符號,修飾int 、char
- long------長型,修飾int 、double
- short------短型,修飾int
2.1 無符號整型(unsigned int)
(1)我們都知道整型是4個字節(有些編譯器不同,可能會是2個),即32位,無符號整型當然也為32位。
(2)既然是32位,無符號整型的取值是32個032個1,即:**04294967295**
(3) 我們舉個例子:32位有點長,所以我們拿16位的unsigned short int 來舉例。
short int 是16位的,無符號的范圍是0~65535。就拿十進制的32767來說,它的二進制為:0111 1111 1111 1111
對於無符號的整型32767來說,它的二進制的最高位稱為數據位,即那個0就是數據位,數據位是要參與運算的,如果我們把0改成1,即16個1,它的十進制就是65535(就是2的15次方+2的14次方...一直加到2的0次方),這是不同於有符號整型的。
(4) 為了進行理解(3)中的含義,做一個程序說明:
#include <stdio.h>
main()
{
unsigned short int a=32767, b=a+1;//定義短整型無符號
printf("a=%u\nb=%u\n",a,b);//以無符號輸出
}
定義的時候a=32767,也就是0111 1111 1111 1111,輸出的依然是32767,a+1=32768, 二進制為1000 0000 0000 0000,輸入依然為32768。根據(3)中講解的,無符號整型的二進制最高位為數據位,數據位為0為1都是按照正常來算的。
2.1 有符號整型(signed int)
(1)有符號整型也是32位;
(2)它的取值范圍就與無符號整型不同了。它的范圍是-2147483648~2147483647這個范圍可以理解為無符號整型的一半變成了負數;
(3) 我們舉個例子:32位有點長,所以我們拿16位的unsigned short int 來舉例。
short int 是16位的,有符號的范圍是-32768~32767。這個時候可能就有人發問了,32768用二進制表示為1000 0000 0000 0000,那么這個負的32768的負號又怎么理解呢?看下面。
還是以32767為例子,它的二進制為:0111 1111 1111 1111。對於有符號整型32767來說,它的二進制最高位稱為符號位(而不是數據位了),符號位顧名思義就是決定正負號的,規則:0是正,1為負。
(4) 為了進行理解(3)中的含義,做一個程序說明:
#include <stdio.h>
main()
{
// 定義有符號類型
short int a=32767, b, c, d;
b=a+1;
c=a+2;
d=a+3;
printf("a=%d\nb=%d\nc=%d\nd=%d\n",a,b,c,d);
}
(5)了解了什么是補碼后再來看上述程序:32767的二進制為:0111 1111 1111 1111。我們來計算一下c的值為什么會等於-32767。c=32767+2,c的二進制為:1000 0000 0000 0001(32767的二進制加2),c的這個二進制是在計算機中存儲的補碼,需要將它轉換為原碼,也就是將c的二進制數減一再取反。得到的二進制原碼為:1111 1111 1111 1111。我們已經說過,符號位為1,表示負值,並不參加運算,所以此二進制的十進制為:-32767。
(6)通過程序也可以發現一個規律,short int的取值范圍是-32768~32767,把頭尾連接起來形成一個環就可以了。