1. 程式人生 > >原碼、反碼、補碼及位操作符,C語言位操作

原碼、反碼、補碼及位操作符,C語言位操作

  • 計算機中的所有資料均是以二進位制形式儲存和處理的。所謂位操作就是直接把計算機中的二進位制數進行操作,無須進行資料形式的轉換,故處理速度較快。

1、原碼、反碼和補碼

  • 位(bit)
    • 是計算機中處理資料的最小單位,其取值只能是 0 或 1。
  • 位元組(Byte)
    • 是計算機處理資料的基本單位,通常系統中一個位元組為 8 位。即:1 Byte=8 bit。
  • 為便於演示,本節表示的原碼、反碼及補碼均預設為 8 位。
  • 準確地說,資料在計算機中是以其補碼形式儲存和運算的。在介紹補碼之前,先了解原碼和反碼的概念。
  • 正數的原碼、反碼、補碼均相同。
  • 原碼:
    • 用最高位表示符號位,其餘位表示數值位的編碼稱為原碼。其中,正數的符號位為 0,負數的符號位為 1。
  • 負數的反碼:
    • 把原碼的符號位保持不變,數值位逐位取反,即可得原碼的反碼。
  • 負數的補碼:
    • 在反碼的基礎上加 1 即得該原碼的補碼。
  • 例如:
    • +11 的原碼為: 0000 1011
    • +11 的反碼為: 0000 1011
    • +11 的補碼為: 0000 1011
    • -7 的原碼為:1000 0111
    • -7 的反碼為:1111 1000
    • -7 的補碼為:1111 1001
  • 注意,對補碼再求一次補碼操作就可得該補碼對應的原碼。

2、位操作符

  • 語言中提供了 6 個基本的位操作符,如表 2 所示。
  • 注意,計算機中位運算操作,均是以二進位制補碼形式進行的。
  • 2.1 按位與(&)

    • 只有兩位同時為 1 時,結果才為 1;只要兩位中有一位為 0,則結果為 0。用式子表示為:
    0 & 0 = 0
    0 & 1 = 0
    1 & 0 = 0
    1 & 1 = 1
    • 複合賦值運算子:&= 表示按位與後賦值。
    • 例如,計算 20 和 9 按位與的結果,如下所示。
    • 即:20&9=0。
    • 應用一:使用 0x01 與一個數按位與,可獲取該數對應二進位制數的最低位。
    • 應用二:使用 0x00 與一個數按位與,可使該數低位的一個位元組清零。
    • 例如,9&0x1 可求得 9 對應二進位制數 0000 1001 的最低位 1。
    • 【例 1】分析以下程式的功能,並輸出其執行結果。
    #include<stdio.h>
    int main (void)
    {
    int n;
    for(n=1;n<=20;n++)
      if (0==(n&0x1))
                printf("%d ",n);
        printf ("\n");
        return 0;
    }
    • 程式執行結果為:
    2 4 6 8 10 12 14 16 18 20
    • 程式分析:
    • n&0x1 的功能是取出 n 對應補碼二進位制數的最低位(最右端位),如果該位為 0,則輸出。二進位制數 bn-1bn-2bn-3…b2b1b0。對應的十進位制數 N 的表示式為:
    • N=b0 X 20 + b1 X 21 + b2 X 22 + b3 X 23 + b4 X 24 + …
    • 由於從上式中第二項開始的每一項都是偶數,故N是否偶數取決於 b0 是否偶數,故 b0 為 1 時是奇數,為 0 時是偶數。
  • 2.2 按位或(丨)

    • 只要兩位中有一位為 1,結果為 1;只有兩位同時為 0 時,結果才為 0。用式子表示為:
    0 | 0 = 0
    0 | 1 = 1
    1 | 0 = 1
    1 | 1 = 1
    • 複合賦值運算子:|= 按位或後賦值。
    • 例如,計算 20 和 9 按位或的結果,如下所示。
    • 即: 20 | 9 = 29。
  • 2.3 按位異或(^)

    • 當兩位相同時,即同為 1 或同為 0 時,結果為 0;當兩位相異時,即其中一位為 1,另一位為 0 時,結果為 1。即相同為 0,相異為 1。用式子表示為:
    0 ^ 0 = 0
    0 ^ 1 = 1
    1 ^ 0 = 1
    1 ^ 1 = 0
    • 由此可得按位異或的 6 個性質或特點如下。
      • a^0=a。即0與任意數按位異或都得該數本身。
      • 1 與任意二進位制位按位異或都得該位取反(0 變 1,1 變 0)。
      • a^a=0。即任意數與自身按位異或都得0。
      • a^b=b^a。即滿足交換律。
      • (a^b)^c=a^(b^c)。即滿足結合律。
      • a^b^b=a^(b^b)=a^0=a。
      • 複合賦值運算子:^= 按位異或後賦值。
    • 例如,計算 22 和 7 按位異或的結果,如下所示。
    • 即:22^7=17。
    • 【例 2】分析以下程式的功能。
    #include<stdio.h>
    int main (void)
    {
        int a=3,b=5;
        a=a^b;
        b=a^b;
        a=a^b;
        printf("a=%d,b=%d\n",a,b);
        return 0;
    }
    • 執行結果:
    a=5,b=3
    • 程式分析:
      • 本題是對按位異或的性質和特點的綜合運用,由於沒有使用中間變數,故在理解上存在一定的難度。
      • 由於 a=a^b; 故:
      • b=a^b=a^b^b=a^(b^b)=a^0=a,即:b=3。
      • a=a^b=(a^b)^a=(b^a)^a=b^(a^a)=b^0=b,即:a=5。
      • 故實現了 a 與 b 的交換。
  • 2.4 左移(<<)

    • 將運算數的各二進位制位均左移若干位,高位丟棄(不包含 1),低位補 0。左移時捨棄的高位不包含 1,則每左移一位,相當於該數乘以 2。
    • 複合賦值運算子: <<= 左移後賦值。
    • 例如,計算 10 左移兩位的結果,如下所示。
    • 丟棄左邊高位移出去的 0,低位補 0。
    • 左移一位相當於該數乘以 2,本例中左移兩位,故相當於乘以 4。即:10<<2 = 10 X 2 X 2 = 40。
  • 2.5 右移(>>)

    • 將運算數的各二進位制位全部右移若干位,正數左補 0,負數左補 1,右邊移出的位丟棄。
    • 複合賦值運算子: >>= 右移後賦值。
    • 例如,計算 70 右移兩位的結果,如下所示。
    • 丟棄右邊移出去的所有位,由於該數為正數,左邊補 0。
    • 右移一位相當於該數除以 2 取整,本例中右移兩位,故相當於除以 4 取整。即:70>>2=70/4 = 17。
  • 2.6 按位取反(~)

    • 0 變 1,1 變 0。用式子表示為:
    ~0 = 1
    ~1 = 0
    • 應用:~a+1=-a 即對任意數按位取反後加 1,得該數的相反數。
    • 例如,計算 10 按位取反的結果,如下所示:
    • 由於計算機中位運算均是以補碼形式操作的,正數的補碼是其本身,負數的補碼為其反碼加 1。
    • 所得顯然是負數的補碼,對補碼 1111 0101 再做一次求補操作,即可得該補碼對應的原碼。 求 1111 0101 補碼的過程如下所示。
      • 反碼 1000 1010 --符號位 1 保持不變,數值位按位取反
      • 補碼 1000 1011 --反碼加1
      • 根據 (補碼)補碼=原碼
      • 故補碼1111 0101對應的原碼為1000 1011=-11,即:~(10)D =~(0100 0110)B補= (1111 0101)B補=-11
      • 由此可見,~10+1=-11+1=-10,即滿足 ~a+1=-a。