開發(fā)基礎(chǔ)隨筆之位運(yùn)算符(Bitwise Operators)

  • 位運(yùn)算符霍转,屬于算術(shù)運(yùn)算符
  1. 按位邏輯運(yùn)算符:
~ 按位非   ~a   a的按位非
& 按位與   a&b  a和b的按位與
| 按位或     a|b   a和b的按位或
^ 按位異或    a^b   a和b的按位異或
  1. 位移運(yùn)算符:
<< 按位左移   a<<b   a左移b位
>> 按位右移   a>>b   a右移b位
  • 位運(yùn)算符的運(yùn)算數(shù)只能是整數(shù)

位移運(yùn)算符:按位左移
  • a<<ba左移b位鲁捏,低位補(bǔ)0
int a = 5;
printf("%d", a << 3); // 40

aint變量鹊汛,占據(jù)32位、4個(gè)字節(jié):

00000000 00000000 00000000 00000101 // 5

a左移3位州丹,相當(dāng)于讓a這一串整體向左挪動(dòng)醋安,結(jié)果相當(dāng)于在0...101后面補(bǔ)上3個(gè)0

00000000 00000000 00000000 00000101 000 

但是由于4個(gè)字節(jié)只有32位,因此將開頭的3個(gè)0舍去墓毒,最終變成:

00000000 00000000 00000000 00101000 // 40

如果是負(fù)數(shù)的情況例子??:

int a = -5;
printf("%d", a << 3); // -40
11111111 11111111 11111111 11111011 // -5
11111111 11111111 11111111 11111011 000
11111111 11111111 11111111 11011000 // -40
  • 有什么規(guī)律:
    整數(shù)5向左移3位后吓揪,20 + 22 變成 23 + 25 = (20 + 22) * 23
    所以a << b 的運(yùn)算結(jié)果跟 a * 2b 一樣
位移運(yùn)算符:按位右移
  • a>>ba右移b位,高位用符號(hào)位填充
int a = 40;
printf("%d", a >> 3); // 5
00000000 00000000 00000000 00101000 // 40

40右移3位所计,相當(dāng)于整體往右邊移動(dòng)柠辞,這時(shí)候就左邊補(bǔ)上3個(gè)0,因?yàn)?code>a是正數(shù)醉箕,所以補(bǔ)充的是0

000 00000000 00000000 00000000 00101000

同樣的由于4個(gè)字節(jié)只有32位钾腺,這時(shí)候右邊的就要舍棄3位:

00000000 00000000 00000000 00000101 // 5

如果是負(fù)數(shù)的情況例子??:

int a = -40;
printf("%d", a >> 3); // 5-

由于a是負(fù)數(shù),所有左邊最高位就是用1來填補(bǔ):

11111111 11111111 11111111 11011000 // -40
111 11111111 11111111 11111111 11011000 
11111111 11111111 11111111 11111011 // -5
  • 有什么規(guī)律:
    整數(shù)40向右移3位后讥裤,由 23 + 25 變成了 20 + 22 = (23 + 25) * 2-3 = (23 + 25) / 23
    所以所以a >> b 的運(yùn)算結(jié)果跟 a / 2b 一樣
應(yīng)用例子??:計(jì)算a的兩倍

之前做法是:

int a = 2;
printf("%d", a * 2); // 4

現(xiàn)在用位移來實(shí)現(xiàn):直接向左移1

int a = 2;
printf("%d", a << 1); // 4

同樣的放棒,計(jì)算下a的一半,之前做法是用除法/己英,現(xiàn)在通過右移實(shí)現(xiàn):

int a = 2;
printf("%d", a >> 1); // 1

按位邏輯運(yùn)算符

  • 按位非间螟,也叫做按位取反
    ~a:將a的所有二進(jìn)制位(包括符號(hào)位)取反,01损肛,10
// 00000000 00000000 00000000 00000000
int a = 0;
// 11111111 11111111 11111111 11111111
printf("%d", ~a); // -1
// 00000000 00000000 00000000 00000001
int a = 1;
// 11111111 11111111 11111111 11111110
printf("%d", ~a); // -2
// 00000000 00000000 00000000 00010011
int a = 19;
// 11111111 11111111 11111111 11101100
printf("%d", ~a); // -20
// 11111111 11111111 11111111 11101100
int a = -20;
// 00000000 00000000 00000000 00010011
printf("%d", ~a); // 19
  • 按位與
    a & b:將每個(gè)二進(jìn)制位的數(shù)值進(jìn)行對(duì)比
    只有當(dāng)2個(gè)二進(jìn)制位都為1時(shí)厢破,運(yùn)算結(jié)果才為1
    只要有1個(gè)二進(jìn)制位為0,運(yùn)算結(jié)果就為0
int a = 140;
int b = 200;
printf("%d", a & b); // 136
/*
a:     00000000 00000000 00000000 10001100  // 140
b:     00000000 00000000 00000000 11001000  // 200
a & b: 00000000 00000000 00000000 10001000  // 136
*/
  • 按位或
    a | b:將每個(gè)二進(jìn)制位的數(shù)值進(jìn)行對(duì)比
    只要有1個(gè)二進(jìn)制位為1治拿,運(yùn)算結(jié)果就為1
    只有當(dāng)2個(gè)二進(jìn)制位都為0時(shí)候摩泪,運(yùn)算結(jié)果才為0
int a = 140;
int b = 200;
printf("%d", a | b); // 204
/*
a:     00000000 00000000 00000000 10001100  // 140
b:     00000000 00000000 00000000 11001000  // 200
a & b: 00000000 00000000 00000000 11001100  // 204
*/
  • 按位異或
    a ^ b:將每個(gè)二進(jìn)制位的數(shù)值進(jìn)行對(duì)比
    當(dāng)2個(gè)二進(jìn)制位的值不相等時(shí),運(yùn)算結(jié)果就為1
    當(dāng)2個(gè)二進(jìn)制位的值相等時(shí)劫谅,運(yùn)算結(jié)果才為0
int a = 140;
int b = 200;
printf("%d", a ^ b); // 68
/*
a:     00000000 00000000 00000000 10001100  // 140
b:     00000000 00000000 00000000 11001000  // 200
a & b: 00000000 00000000 00000000 01000100  // 68
*/

特點(diǎn):
a ^ 0:結(jié)果為a
a ^ a:結(jié)果為0
a ^ b=b ^ a
a ^ b ^ c=a ^ (b ^ c)=(a ^ c) ^ b:多個(gè)值進(jìn)行異或见坑,值的位置順序不影響


與位運(yùn)算符相關(guān)的賦值運(yùn)算符


按位與&判斷奇偶性

一般我們判斷一個(gè)數(shù)的奇偶,通常都是用這個(gè)數(shù)跟2取余捏检,余數(shù)為0是偶數(shù)荞驴,否則是奇數(shù):

int a = 26;
printf("%s", (a % 2) ? "奇數(shù)" : "偶數(shù)"); // 偶數(shù)

int b = 25;
printf("%s", (b % 2) ? "奇數(shù)" : "偶數(shù)"); // 奇數(shù)

現(xiàn)在還可以用按位與&來判斷奇偶:

int a = 26;
printf("%s", (a & 1) ? "奇數(shù)" : "偶數(shù)"); // 偶數(shù)
        
int b = 25;
printf("%s", (b & 1) ? "奇數(shù)" : "偶數(shù)"); // 奇數(shù)

根據(jù)按位與的規(guī)則,我們來看下:任何數(shù)跟1進(jìn)行按位與操作贯城,就看最后一位熊楼,二進(jìn)制位最低位是1,結(jié)果就是1能犯,如果是0鲫骗,結(jié)果就是0犬耻。

    0b 1100 (12)
&   0b 0001 (1)
=   0b 0000 (0)
         
    0b 1101 (13)
&   0b 0001 (1)
=   0b 0001 (1)

所以只要看下a的最低位是什么,就能知道奇偶挎峦。不管你左邊的二進(jìn)制位數(shù)有多少1或者0(最終換成十進(jìn)制相加肯定是偶數(shù))香追,這時(shí)候只要你最低位是1120 = 1)最終換成十進(jìn)制最后一位是+ 1必定奇數(shù)合瓢;如果最低位是0坦胶,偶數(shù)+ 0依然是偶數(shù)。


不使用第三方變量交互兩個(gè)整型變量的值

一般需要交互兩個(gè)數(shù)的值晴楔,我們會(huì)通過一個(gè)臨時(shí)的值來進(jìn)行交互:

int a = 10;
int b = 20; 
        
int temp = a; // 10
a = b; // 20
b = temp; // 10
printf("%d  %d\n", a, b); // 20 10

但是這種方法需要多聲明一個(gè)變量顿苇,無形中浪費(fèi)了內(nèi)存,那么如何不通過第三方變量進(jìn)行交換:

a = a + b; // 30 = 10 + 20
b = a - b; // 10 = 30 - 20
a = a - b; // 10 = 30 - 10
printf("%d  %d\n", a, b);
a = a * b; // 200 = 10 * 20
b = a / b; // 10 = 200 / 20
a = a / b; // 20 = 200 / 10
printf("%d  %d\n", a, b);
a = a - b; // -10 = 10 - 20
b = a + b; // 10 = -10 + 20
a = b - a; // 20 = 10 - -10
printf("%d  %d\n", a, b);

上面借助兩個(gè)變量間的加減乘除計(jì)算税弃,來交互兩個(gè)值纪岁。這種做法有不足,整型在計(jì)算的時(shí)候容易造成值溢出或者精度丟失则果;加減乘容易導(dǎo)致溢出幔翰,除法會(huì)導(dǎo)致精度丟失。
通過按位異或^直接操作二進(jìn)制位進(jìn)行交互西壮,不會(huì)有上述的問題遗增。

a = a ^ b; 
b = a ^ b; 
a = a ^ b; 
printf("%d  %d\n", a, b);

我們知道異或有幾個(gè)特點(diǎn):
a ^ 0:結(jié)果為a
a ^ a:結(jié)果為0
a ^ b=b ^ a
a ^ b ^ c=a ^ (b ^ c)=(a ^ c) ^ b
那么第二步中的

b = a ^ b;  等價(jià)于  10 ^ 20 ^ 20 ===  10 ^ 0 === 10

第三步中的

a = a ^ b;  等價(jià)于  10 ^ 20 ^ 10 ===  10 ^ 10 ^ 20 === 0 ^ 20 === 20

最終ab實(shí)現(xiàn)了交互
又因?yàn)榘次划惢蛸x值符號(hào)^=的存在款青,我們可以進(jìn)行更簡潔的寫法:

a ^= b; // a = a ^ b
b ^= a; // b = b ^ a = a ^ b
a ^= b; // a = a ^ b
printf("%d  %d\n", a, b);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末做修,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抡草,更是在濱河造成了極大的恐慌饰及,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件康震,死亡現(xiàn)場離奇詭異燎含,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)腿短,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門屏箍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人答姥,你說我怎么就攤上這事铣除。” “怎么了鹦付?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵尚粘,是天一觀的道長。 經(jīng)常有香客問我敲长,道長郎嫁,這世上最難降的妖魔是什么秉继? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮泽铛,結(jié)果婚禮上尚辑,老公的妹妹穿的比我還像新娘。我一直安慰自己盔腔,他們只是感情好杠茬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弛随,像睡著了一般瓢喉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舀透,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天栓票,我揣著相機(jī)與錄音,去河邊找鬼愕够。 笑死走贪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惑芭。 我是一名探鬼主播坠狡,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼强衡!你這毒婦竟也來了擦秽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤漩勤,失蹤者是張志新(化名)和其女友劉穎感挥,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體越败,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡触幼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了究飞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片置谦。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖亿傅,靈堂內(nèi)的尸體忽然破棺而出媒峡,到底是詐尸還是另有隱情,我是刑警寧澤葵擎,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布谅阿,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏签餐。R本人自食惡果不足惜寓涨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望氯檐。 院中可真熱鬧戒良,春花似錦、人聲如沸冠摄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耗拓。三九已至拇颅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乔询,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工韵洋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留竿刁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓搪缨,卻偏偏與公主長得像食拜,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子副编,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354