c語言學習之位運算知識總結和實例分析

先給大家講個笑話吧,說世界上有10種人,一種知道二進制,而另一種不知道二進制。
位運算的基礎就是二進制。

二進制的位運算包括五種運算:

與,或,異或,左移,右移
與(&) 0&0 = 0,0&1 = 0;1&0 = 0;1&1 = 1;
或(|) 0|0 = 0, 0|1 = 1; 1|0 = 1; 1|1 = 1;
異或(^)0^0 =0; 0^1 = 1; 1^0 = 1; 1^1 = 0;
左移(<<) 00001001 << 2 =00100100
右移(>>) 10001010 >> 3 = 00010001
前面已經提到過,右移分爲算術右移和邏輯右移,主要在於無符號數和有符號數的區別。
邏輯右移:右移後,左端補零
00001010 >> 2 = 00000010
算術右移:右移後,左端補最高位數
有符號數10001010 >>3 = 11110001
對於無符號數按照邏輯右移,有符號數一般爲算術右移

1.位運算的特點

1,按位與(&)
清零特定位,10001010 & 01111111 = 00001010 (首位清零)
取指定位, 10001010 & 10000000 = 10000000 (取首位)
2,按位或(|)
將某一位置置一,10001010 | 00000001 = 10001011 (末尾置一)
3,位異或(^)
使特定位取反 ,10001010 ^ 10001010 = 00001010 (首位取反)
不引入第三變量,交換兩個變量的值
a = a^b ;b = a^b;a = a^b

4,補碼運算
-x = ~x +1

2.位運算的應用

應用舉例:
1,判斷int型變量a 是奇數還是偶數
a & 1 = 0 偶數(末尾爲零)
a & 1 = 1 奇數
2,取變量的第k位 a>> k & 1
3,將變量的第k位清零,a = a& ~(1<< k)
4,將變量的第k 位置1,a = a |(1<< k)
5,將變量循環左移k次,就是從左移出的從右端進,a = a << k | a >>16-k 設(sizeof(int) = 16);
6,整數的平均值
對於兩個整數x,y ,如果用(x + y)/2求平均值,會產生溢出,因爲x+y可能超出範圍。
int average(int x,int y)
{
return (x&y) +((x^y) >> 1);
}
7,乘法轉換爲位運算
a *(2^n) = a << n
8,除法運算轉換爲位運算
a/(2^n) = a>> n

3.由計算絕對值引出補碼,原碼,反碼,有符號數的討論

int abs(int x)
{
int y;
y = x>> 31; (若x爲負數,1111111111111111)有符號數爲-1
return (x^y)-y; //
假如x= -2;(1111111111111110)
y= 1111111111111111
x^y = 0000000000000001
x^y-y = 0000000000000010 表示+2
如果求原碼:如果是求原碼,符號位不變,其他位按位取反,再加一
~x=1000000000000001
~x+1 = 1000000000000010
原碼:符號+值,表示-2。
上述求絕對值方法和求原碼的方法類似,相當於是把原碼的符號位從1變爲零,如果是正數,則不變,這一些都是由x>>31,獲得符號位開始。正數的補碼=原碼=絕對值
注意:原碼和補碼只是有符號數的兩種不同的表示方式。
求補碼:
若x < 0,x = x+2^w; 若x> 0,x = x
x = -1,補碼爲(1111111111111111),無符號數表示的 Umax和補碼錶示的-1有相同的位模式。

4.c與指針第五章編程練習三

題目:請編寫函數
unsigned int reverse_bit(unsigned int value);
這個函數的返回值是把value的二進制模式從左到右變換一下後的值,例如在32位機器上,25這個值包含下列各個位:
00000000000000000000000000011001
函數的返回值是2550136832,它的二進制模式是:
10011000000000000000000000000000
編寫函數時要注意不要讓它依賴於你的機器上整型值的長度

unsigned int reverse_bit(unsigned int value)
{
    unsigned int answer;
    unsigned int i;
    answer = 0;
    for(i = 1;i != 0;i <<=1)  //通過是1一直左移到越過最高位,不依賴機器長度
    {
        answer << 1;   //左移挪出最右端位置
        if(value & 1 != 0) //如果value最後一位爲1
            answer |= 1;   //就把這個1放在answer的最右端
        value >> 1;   //value已經被挪走的數就丟棄掉
    }
    return answer;
}
//利用的是從value右端出數,然後從answer右端進數,然後再左移,就將二進制位模式順序改變。

5.劍指offer 面試題10 求二進制中1的個數

題目:請實現一個函數,輸入一個整數,輸出該數二進制表示中1的個數,例如把9表示成二進制是1001,有2位1
常規思路:將整數右移一次,然後&1,判斷最後一位是否爲1,爲1,則計數加1.
代碼如下:

int NumberOf1(int n)
{
    int count =0;
    while(n)
    {
        if(n & 1 )
            count ++;
        n >>=1;
    }
    return count;
}

看起來似乎很合理,但是如果n爲負數,作爲有符號數,右移將在左端補1,一直右移,將陷入死循環。
爲了避免死循環,我們可以不右移輸入的數字n。

int NumberOf1(int n)
{
    int count = 0;
    unsigned int flag = 1;
    while(n)
    {
        if(n & flag)
            count++;
        flag << = 1;
    }
    return count;
}

32位的整數需要循環32次,效率不高
方案三:

int NumberOf1(int n)
{
    int count = 0;
    while(n)
    {
        ++count;
        n = (n-1) & n;
    }
    return count;
}

關鍵點就是把一個整數減去1之後再和原來的整數做位與運算,得到的結果相當於是把整數的二進制表示中的最右邊一個1變成0.

發佈了47 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章