位運(yùn)算符詳解
前言:由于位運(yùn)算符是直接對二進(jìn)制數(shù)操作,因此對二進(jìn)制、八進(jìn)制、十六進(jìn)制不甚了解的小伙伴建議先看這篇二進(jìn)制、八進(jìn)制、十六進(jìn)制與十進(jìn)制的相互關(guān)系,這樣閱讀本篇時將事半功倍
總覽
位運(yùn)算是對計(jì)算機(jī)存儲的二進(jìn)制序列的相應(yīng)位進(jìn)行操作
位運(yùn)算的操作數(shù)必須是整數(shù)型或字符型
運(yùn)算符 示例 按位與 & a & b 按位或 | a | b 按位異或 ^ a ^ b 按位取反 ~ ~ a 左移 << a << b 右移 >> a >> b
按位與 &
按位與是指:參加運(yùn)算的兩個操作數(shù),按二進(jìn)制對應(yīng)的位進(jìn)行“與”運(yùn)算。如果兩個相應(yīng)的二進(jìn)制位都為1,則得到的結(jié)果為1,否則位0。
0 & 0 = 0,0 & 1 = 0,1 & 1 = 1,1 & 0 = 0,即 全一為1,有零則零
int a = 15; //15的二進(jìn)制序列為 0000 0000 0000 0000 0000 0000 0000 1111 int b = 17; //16的二進(jìn)制序列為 0000 0000 0000 0000 0000 0000 0001 0001 int c = a & b; //a和b按位與,那么得到的二進(jìn)制序列應(yīng)該是 0000 0000 0000 0000 0000 0000 0000 0001 //即十進(jìn)制的1 printf("c = %d\n", c);
按位或 |
兩個相應(yīng)的二進(jìn)制位中只要有一個為1,則得到的結(jié)果為1。
0 | 0 = 0,0 | 1 = 1,1 | 0 = 1,1 | 1 = 1,即 有一為1,全零為0
int a = 15; //15的二進(jìn)制序列為 0000 0000 0000 0000 0000 0000 0000 1111 int b = 17; //16的二進(jìn)制序列為 0000 0000 0000 0000 0000 0000 0001 0001 int c = a | b; //a和b按位與,那么得到的二進(jìn)制序列應(yīng)該是 0000 0000 0000 0000 0000 0000 0001 1111 //即十進(jìn)制的31 printf("c = %d\n", c);
按位異或 ^
參加運(yùn)算的兩個二進(jìn)制位,如果相同則為0,不同則為1
1 ^ 1 = 0,1 ^ 0 = 1,0 ^ 1 = 1,0 ^ 0 = 0,即 不同為1,相同為0
int a = 15; //15的二進(jìn)制序列為 0000 0000 0000 0000 0000 0000 0000 1111 int b = 17; //16的二進(jìn)制序列為 0000 0000 0000 0000 0000 0000 0001 0001 int c = a ^ b; //a和b按位與,那么得到的二進(jìn)制序列應(yīng)該是 0000 0000 0000 0000 0000 0000 0001 1110 //即十進(jìn)制的30 printf("c = %d\n", c);
按位取反 ~
將二進(jìn)制中的每個位的 0變成1,1變成0
如 ~025就是對八進(jìn)制數(shù)25(即二進(jìn)制數(shù)000 010 101)進(jìn)行按位取反,得到752(111 101 010)
int a = 15; //15的二進(jìn)制序列為 0000 0000 0000 0000 0000 0000 0000 1111 int b = ~ a; //a按位取反,二進(jìn)制序列為 1111 1111 1111 1111 1111 1111 1111 0000 // 由于在計(jì)算機(jī)內(nèi)部是按補(bǔ)碼儲存,那么轉(zhuǎn)換為反碼 1111 1111 1111 1111 1111 1111 1110 1111 // 轉(zhuǎn)化為原碼 1000 0000 0000 0000 0000 0000 0001 0000 //即十進(jìn)制的-16 printf("b = %d\n", b); int c = -1; //-1的二進(jìn)制序列為 1111 1111 1111 1111 1111 1111 1111 1111 int d = ~c; //c按位取反,二進(jìn)制序列為 0000 0000 0000 0000 0000 0000 0000 0000 //即十進(jìn)制的0 printf("d = %d\n", d);
左移 <<
左移運(yùn)算符是用來將一個數(shù)的各二進(jìn)制位全部向左移動若干位,右邊的空位補(bǔ)零
例如 a = 3(10) = 011(2),那么 a << 1的結(jié)果就是110(2)=6(10)
int a = 17; //a的二進(jìn)制序列為 0000 0000 0000 0000 0000 0000 0001 0001 int b = a << 2; //b為所有二進(jìn)制為左移兩位得到 0000 0000 0000 0000 0000 0000 0100 0100 //即十進(jìn)制數(shù) 68 printf("b = %d\n", b);
右移 >>
- 右移運(yùn)算符是用來將一個數(shù)的各二進(jìn)制位全部向右移動若干位
- 邏輯右移:右移之后的空位無論原符號位是什么,統(tǒng)一補(bǔ)零
- 算術(shù)右移:如果原符號位位1,那么空位全部補(bǔ)1,反之如果符號位為0,那么空位補(bǔ)0
- 一般來說,編譯器采用的是算術(shù)右移
int a = 17; //a的二進(jìn)制序列為 0000 0000 0000 0000 0000 0000 0001 0001 int b = a >> 2; //b為所有二進(jìn)制為左移兩位得到 0000 0000 0000 0000 0000 0000 0000 0100 //即十進(jìn)制數(shù)4 int c = -15; //c的二進(jìn)制原碼序列為 1000 0000 0000 0000 0000 0000 0000 1111 //反碼 1111 1111 1111 1111 1111 1111 1111 0000 //補(bǔ)碼 1111 1111 1111 1111 1111 1111 1111 0001 int d = c >> 2; //如果是邏輯右移 //d的二進(jìn)制序列為 0011 1111 1111 1111 11111 1111 1111 1100 //如果是算術(shù)右移 //d的二進(jìn)制序列為 1111 1111 1111 1111 1111 1111 1111 1100 //反碼 1111 1111 1111 1111 1111 1111 1111 1011 //原碼 1000 0000 0000 0000 0000 0000 0000 0100 //即十進(jìn)制數(shù)-4 printf("b = %d\n", b); printf("d = %d\n", d);
位運(yùn)算的運(yùn)用
判斷一個數(shù)是否為偶數(shù)
Tips:利用位運(yùn)算對一個數(shù)進(jìn)行奇偶判斷的速度要快于if(num % 2 == 0){……}這一操作
我們知道二進(jìn)制數(shù)每一位的權(quán)重為2,因此要判斷一個數(shù)是否為偶數(shù),只需要判斷它二進(jìn)制位的第一位是1還是0
- 如果第一位是1,那么十進(jìn)制值就會加上20 = 1這個奇數(shù),而偶數(shù)加奇數(shù)一定為奇數(shù),因此該數(shù)一定是奇數(shù)
- 如果第一位是0,那么這個1就不會加上,該數(shù)就一定是偶數(shù)
那我們?nèi)绾蔚玫蕉M(jìn)制數(shù)的第一位呢?將這個數(shù)和1進(jìn)行按位與操作就可以了,因?yàn)?的二進(jìn)制位只有第一位為1,這樣我們就可以單獨(dú)對二進(jìn)制的第一位進(jìn)行判斷,從而判斷該數(shù)的奇偶性
#include<stdio.h> int main() { int a; while (scanf_s("%d", &a) != EOF) { //注意:關(guān)系運(yùn)算符的優(yōu)先級高于&,|,^操作符 if ((a & 1) == 0) printf("%d是偶數(shù)\n",a); else printf("%d是奇數(shù)\n", a); } return 0; }
統(tǒng)計(jì)二進(jìn)制數(shù)中1的個數(shù)
方法一
我們知道如果我們要獲得十進(jìn)制數(shù)的每一位,我們可以對這個數(shù)不斷進(jìn)行模10除以10操作
- 例如要獲得十進(jìn)制數(shù)139的每一位,139 % 10 = 9,得到各位9,139 / 10 = 13,13 % 10 = 3,得到十位3,13 / 10 = 1,1 % 10 = 1,得到百位1
那么對二進(jìn)制數(shù)也可以這樣操作,只是將模10除以10改為模2除以2
int One_Count(unsigned int num) //參數(shù)定義為無符號型是為了確保對負(fù)數(shù)運(yùn)算的正確 { int count = 0; while (num) { if (num % 2 == 1) count++; num /= 2; } return count; }
方法二
可以利用 & 獲得二進(jìn)制的每一位
int One_Count(int num) { int count = 0; for (int i = 0; i < 32; i++) { if (((num >> i) & 1) == 1) count++; } return count; }
方法三(巧解)
可能有小伙伴會認(rèn)為上面的方法二中的循環(huán)固定要循環(huán)32次,當(dāng)計(jì)算小數(shù)字二進(jìn)制中1的個數(shù)時會浪費(fèi)時間,那么還有沒有更加極致的方法呢?
有!我們來看一個表達(dá)式:n = n & (n - 1)
舉個例子,n = 15(10) = 1111(2), n - 1 = 14(10) = 1110(2), n = n & (n-1) = 1110(2)
? n = 1110(2), n - 1 = 1101(2), n = n & (n-1) = 1100(2)
? n = 1100(2), n - 1 = 1011(2), n = n & (n-1) = 1000(2)
? n = 1000(2), n - 1 = 0111(2), n = n & (n-1) = 0000(2)
我們可以發(fā)現(xiàn),這個表達(dá)式每一次計(jì)算,都可以將數(shù)n二進(jìn)制位中最右邊的1變成0,因此我們可以創(chuàng)建一個循環(huán),n = n & (n - 1)這個表達(dá)式可以運(yùn)行的次數(shù)就是數(shù)n二進(jìn)制位中含1的個數(shù)
int One_Count(int num) { int count = 0; while (num) { num = num & (num - 1); count++; } return count; }
統(tǒng)計(jì)兩個二進(jìn)制數(shù)中不同位的個數(shù)
根據(jù)按位異或 ^ 的規(guī)律,兩個二進(jìn)制位如果不同,則結(jié)果為1,相同結(jié)果為0,因此我們可以將這兩個數(shù)先按位異或,在統(tǒng)計(jì)結(jié)果二進(jìn)制位中1的個數(shù)
#include<stdio.h> int One_Count(int num) { int count = 0; while (num) { num = num & (num - 1); count++; } return count; } int main() { int a, b, c; scanf_s("%d %d", &a, &b); c = a ^ b; printf("%d和%d二進(jìn)制不同位有%d個\n", a,b,One_Count(c)); return 0; }
將一個數(shù)用二進(jìn)制形式打?。ㄗ筮厼楦呶唬疫厼榈匚唬?/h4>
#include<stdio.h>
void Print(int num)
{
unsigned int temp = 0x80000000;
//即二進(jìn)制數(shù) 1000 0000 0000 0000 0000 0000 0000 0000
//第一位不看成符號位
while (temp)
{
if ((num & temp))
printf("1 ");
else
printf("0 ");
temp >>= 1;
}
}
int main()
{
int a;
scanf_s("%d", &a);
Print(a);
printf("\n");
return 0;
}
不創(chuàng)建臨時變量交換兩個數(shù)
-
這里我們要用到按位異或 ^
-
這種方法的運(yùn)行效率不如創(chuàng)建臨時變量文章來源:http://www.zghlxwxcb.cn/news/detail-452300.html
void exchange(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
-
這種方法僅作了解即可文章來源地址http://www.zghlxwxcb.cn/news/detail-452300.html
#include<stdio.h>
int main()
{
int a = 2, b = 3;
a = a ^ b;
b = a ^ b;
a = a ^ b;
return 0;
}
#include<stdio.h>
void Print(int num)
{
unsigned int temp = 0x80000000;
//即二進(jìn)制數(shù) 1000 0000 0000 0000 0000 0000 0000 0000
//第一位不看成符號位
while (temp)
{
if ((num & temp))
printf("1 ");
else
printf("0 ");
temp >>= 1;
}
}
int main()
{
int a;
scanf_s("%d", &a);
Print(a);
printf("\n");
return 0;
}
-
這里我們要用到按位異或 ^
-
這種方法的運(yùn)行效率不如創(chuàng)建臨時變量文章來源:http://www.zghlxwxcb.cn/news/detail-452300.html
void exchange(int *a, int *b) { int temp = *a; *a = *b; *b = temp; }
-
這種方法僅作了解即可文章來源地址http://www.zghlxwxcb.cn/news/detail-452300.html
#include<stdio.h> int main() { int a = 2, b = 3; a = a ^ b; b = a ^ b; a = a ^ b; return 0; }
到了這里,關(guān)于位運(yùn)算符及其相關(guān)操作詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!