位操作

eiSouthBoy發表於2024-07-07

資料的表示

進位制

對於整數的表示形式有:十進位制、二進位制、八進位制、十六進位制。例如:char a= 17, b = 0B00010001, c = 021, d = 0X11; 其實 a, b, c ,d 是都相等的。寫個demo驗證一下

#include <stdio.h>

int main(int argc, char *argv[])
{
    /*
     * 二進位制字首:  0B 或 0b
     * 八進位制字首:  0 (數字0,不是字母o)
     * 十六進位制字首:0x 或 0X
    */
    char a= 17, b = 0B00010001, c = 021, d = 0X11;

    printf("a: %d\n", a);
    printf("b: %d\n", b);
    printf("c: %d\n", c);
    printf("d: %d\n", d);

    return 0;
}

結果:

其中 char 是一個較小的整型而已,無符號取值範圍:0~255,有符號取值範圍:-127~127。或許你可能會問:為什麼要叫字元型? 因為它主要用用來儲存字元的。這個時候你又會有疑問:char 的取值範圍是 -127~127,儲存又是字元,那麼字元就是是整數? 沒錯,字元就是以整數形式儲存的,字元 'A' ~ 字元 'Z' 對應的整數是 65 ~ 90。字元 'a' ~ 字元 'z' 對應的整數是 97 ~ 122。其他對應的字元可以查 ASCII碼對照表

注意:不管是什麼進製表示,最終在計算機中都是以二進位制取運算和儲存,因為計算機就是處理01011010這樣二進位制資料,什麼十進位制、八進位制、十六進位制僅為了方便開發人員閱讀而已


補碼

對於char 用二進位制表示 0 ~ 127,即 0000 0000 ~ 0111 1111。那麼用二進位制表示 -127 ~ -1 怎麼處理?換一句話說:負數怎麼用二進位制表示? 口訣:負數的原碼取反,再加1

-127 的在計算機的表示形式:補碼,其計算過程如下:

-127的原碼:1111 1111 (左邊第一位是符號位)
-127的反碼:1000 0000 (符號位不變,其他位取反)
-127的補碼:1000 0001 (反碼的基礎上加1)

注意:整數的反碼、補碼都與原碼相同

那麼為什麼整數要用到補碼呢?

  • 可以解決了負數的二進位制表示

  • 消除0 和 -0的歧義,總不能一個0有兩種表示形式吧
    不能char有 0000 0000 和 1000 0000 表示 0 和 -0

  • 使用補碼進行計算

在計算機中整數是如何進行計算的?

使用補碼進行計算的,例如:3-5 的計算過程,當然你可能口算就知道了,但是對於計算來說沒有那麼簡單。

對於3和-5用char型別就可以儲存了。

3的補碼: 0000 0011
-5的補碼:1111 1011

表示式:3 - 5,對於計算機來說就是加法運算,即3 + (-5),用補碼做加法運算結果是:1111 1110(補碼),轉換成十進位制過程,補碼-1,即 1111 1101,然後取反(符號位不變),即1000 0010,就是十進位制的-2

演繹推算完這個過程,覺得計算機的人類先驅真的NB!!! 馮. 諾伊曼 , 圖靈,肯.湯普森,丹尼斯.裡奇,...

位操作

位操作是針對整數的。位操作運算肯定需要位操作運算子:

  • ~:按位取反
  • &:按位與
  • |:按位或
  • ^:按位異或

看到上面這些關鍵詞,這不是數位電路里面的知識嘛,這些東西怎麼跑這來了,仔細想想,計算機玩的不就是數位電路嘛。

寫個demo驗證一下位操作運算子:

#include <stdio.h>

int main(int argc, char *argv[])
{
    // ~ :按位取反,即0變1,1變0
    unsigned char a = 0X0f;
    printf("~(0X0f): 0X%02hhx\n\n", ~a);

    // &:按位與,即相同位置的值都1,結果才為1
    unsigned char b = a & 0Xf0;
    unsigned char c = a & 0Xff;
    printf("0X0f & 0Xf0: 0X%02hhx\n", b);
    printf("0X0f & 0Xff: 0X%02hhx\n\n", c);

    // |:按位或,即相同位置的值有一個為1,結果就為1
    unsigned char d = a | 0Xf0;
    unsigned char e = a | 0Xff;
    printf("0X0f | 0Xf0: 0X%02hhx\n", d);
    printf("0X0f | 0Xff: 0X%02hhx\n\n", e);

    // ^: 按位異或,即相同位置的值不相等,結果就為1
    unsigned char f = a ^ 0Xf0;
    unsigned char g = a ^ 0Xff;
    printf("0X0f ^ 0Xf0: 0X%02hhx\n", f);
    printf("0X0f ^ 0Xff: 0X%02hhx\n\n", g);

    return 0;
}

結果:

掩碼

透過按位與&操作可以檢測某一個位的值是0還是1,例如:0B0000 0101,想檢測第1位的值,可以進行這樣的運算 --> 0B0000 0101 & 0B0000 0010, 若運算結果為非0值,則說明目標的第1位的值是1

透過按位或|操作可以設定某一個位的值是0還是1,例如:0B0000 0101,想設定第1位的值為1,可以進行這樣的運算 --> 0B0000 0101 | 0B0000 0010, 若運算結果為 0B0000 0111,則說明設定成功。

規律總結:

  • 不管該位原來的值是0還是1,它跟0進行&運算,得到的結果都是0,而跟1進行&運算,將保持原來的值不變;

  • 不管該位原來的值是0還是1,它跟1進行|運算,得到的結果都是1,而跟0進行|運算,將保持原來的值不變。

寫個程式碼,驗證一下:

#include <stdio.h>

int main(int argc, char *argv[])
{
    // 檢測第1位的狀態
    unsigned char a = 0B11110010, b = 0B11110000;
    unsigned char mask = 0B00000010;
    printf("0B11110010 & 0B00000010: 0X%02x\n", a & mask);
    printf("0B11110000 & 0B00000010: 0X%02x\n\n", b & mask);

    // 設定第1位的狀態
    unsigned char d = 0B11110010, e = 0B11110011;
    printf("0B11110010 | 0B00000010: 0X%02x\n", d | 0B00000010);
    printf("0B11110011 | 0B00000000: 0X%02x\n\n", e | 0B00000000);

    return 0;
}

結果:

目標匹配

有一個uinst32_t的數,需要驗證:第8位~第12位是00100,第21位是1。

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>

bool checkBits(uint32_t num, int start, int end, uint32_t pattern) 
{
    uint32_t mask = ((uint32_t)1 << (end - start + 1)) - 1; // 建立掩碼
    mask <<= start; // 將掩碼移到正確的位置

    // 使用掩碼將不需要的位清零,然後與pattern進行比較
    return ((num & mask) >> start) == pattern;
}

int target_check(uint32_t *arr, int cnt)
{
    int rc = 0;
    for (int i = 0; i < cnt; i++)
    {
        uint32_t binaryNumber = arr[i];
        bool isPatternCorrect = checkBits(binaryNumber, 8, 12, 0b00100);
        bool isBit21Set = checkBits(binaryNumber, 21, 21, 0b1);
        if (isPatternCorrect && isBit21Set)
        {
            rc = 0;
            printf("%d --> the value is true, (0X%08x)\n\n", i + 1, binaryNumber);
        }
        else
        {
            rc = 1;
            printf("%d --> the value false , (0X%08x)\n\n", i + 1, binaryNumber);
        }
    }
    return rc;
}

int main(int argc, char *argv[])
{
    /*
     * 0B00000000001000000000010000000000, 從右到左,分別是第0位,第1位,... ,第31位。
     * 可以使用無符號32位的整型儲存,  uint32_t num = 0B00000000001000000000010000000000;
     * 32位的二進位制可以用十六進位制表示,uint32_t num = 0X00200400; 即4位二進位制可用1位十六進位制表示
     * 
     * 要求檢測目標數值的第8位至第12位是00100,第21位是1
     */

    uint32_t arr[] = {
        0X00200400,
        0X01200400,
        0Xab300400,
        0X00100400, // error
        0X00200500, // error
        0X00800300, // error
        0B00000000001000000000010000000000,
        0B00000000001000000001010100000000,
    };
    int cnt = sizeof(arr) / sizeof(arr[0]);
    target_check(arr, cnt);

    return 0;
}

結果:

相關文章