深入理解C++移位運(yùn)算符
關(guān)于邏輯移位、算術(shù)移位可參見迅雷深大筆試題部分。的一道題。
以前看到C++標(biāo)準(zhǔn)上說,移位運(yùn)算符(<<、>>)出界時(shí)的行為并不確定:
The behavior is undefined if the right operand is negative, orgreater than or equal to the length in bits of the promoted left operand.
我當(dāng)時(shí)也沒有深究過這個(gè)問題。前幾天有個(gè)網(wǎng)友來信問起這件事,我才發(fā)現(xiàn),這和IntelCPU的移位運(yùn)算有關(guān)。下面是那位網(wǎng)友的來信以及我的回復(fù):
您好!運(yùn)算符<<作為位操作中的高效的操作,但我遇到一個(gè)問題:下面在VC環(huán)境下發(fā)現(xiàn)一個(gè)很不明白的地方,下面標(biāo)注。
#include <stdio.h>
void main() { unsigned int i,j; i=35; //為什么下面兩個(gè)左移操作結(jié)果不一樣? j=1<<i; // j為8 j=1<<35; // j為0 }
不知是哪里沒有理解對(duì)。
原因是這樣的:i=35;j=1<<i;這兩句在VC沒有做優(yōu)化的情況下,將被編譯成下面的機(jī)器指令:
mov dword ptr [i],23h
mov eax,1
mov ecx,dword ptr [i]
shl eax,cl
mov dword ptr [j],eax
在shl一句中,eax=1,cl=35。而IntelCPU執(zhí)行shl指令時(shí),會(huì)先將cl與31進(jìn)行and操作,以限制左移的次數(shù)小于等于31。因?yàn)?5 & 31 =3,所以這樣的指令相當(dāng)于將1左移3位,結(jié)果是8。
而j=1<<35;一句是常數(shù)運(yùn)算,VC即使不做優(yōu)化,編譯器也會(huì)直接計(jì)算1<<35的結(jié)果。VC編譯器發(fā)現(xiàn)35大于31時(shí),就會(huì)直接將結(jié)果設(shè)置為0。這行代碼編譯產(chǎn)生的機(jī)器指令是:
mov dword ptr [j],0
對(duì)上面這兩種情況,如果把VC編譯器的優(yōu)化開關(guān)打開(比如編譯成Release版本),編譯器都會(huì)直接將結(jié)果設(shè)置為0。
所以,在C/C++語(yǔ)言中,移位操作不要超過界限,否則,結(jié)果是不可預(yù)期的。
http://hovertree.com/menu/cpp/
下面是Intel文檔中關(guān)于shl指令限制移位次數(shù)的說明:
The destination operand can be a register or a memory location.The count operand can be an immediate value or register CL. The count is maskedto 5 bits, which limits the count range to 0 to 31. A special opcode encodingis provided for a count of 1.
1.掩碼
就是一串2進(jìn)制 對(duì)目標(biāo)字段進(jìn)行位與運(yùn)算,屏蔽當(dāng)前的輸入位。
將源碼與掩碼經(jīng)過邏輯運(yùn)算得出新的操作數(shù)。其中要用到邏輯運(yùn)算如OR運(yùn)算。AND運(yùn)算。用于如將ASCLL碼中大寫字母改作小寫字母。
2.與 或 異或 轉(zhuǎn)換成補(bǔ)碼運(yùn)算
3. 用法:掩碼 (&)
4. 用法:打開位 (|)
5.用法:關(guān)閉位 (&~)
6. 用法:轉(zhuǎn)置位 (^)
7. 將Value的第bit_number位置1 Value |= 1 << bit_number;
8. 將Value的第bit_number位置0 Value &= ~( 1 << bit_number );
9.value & 1 << bit_number 如果該位置已被置為1,則表達(dá)式的結(jié)果為非零值
C/C ++提供位邏輯運(yùn)算符和移位運(yùn)算符。二者只能用于整形和字符型。位運(yùn)算符是對(duì)每位進(jìn)行操作而不影響左右兩位,這有別于常規(guī)運(yùn)算符(&&|| !)是將整個(gè)數(shù)進(jìn)行操作的。
一. 位邏輯運(yùn)算符
1. ~ 按位取反
將1變?yōu)?,將0變?yōu)?
EG:
~(10011010)
(01100101)
注:
VC++編譯器,計(jì)算~10,得出的結(jié)果是-11。為什么不是5呢
10的二進(jìn)制表示為1010,按位取反應(yīng)該為0101,也就是十進(jìn)制的5,為什么會(huì)得出-11?
VC是32位編譯器,所以
10 = 00000000 00000000 00000000 00001010
~10 = 11111111 11111111 11111111 11110101 = -11
可以通過掩碼(位與) 與15位與
15 = 00000000 00000000 00000000 00001111
~10 = 00000000 00000000 00000000 00000101 = -11
2. & 按位取與
只有兩個(gè)操作數(shù)都是1結(jié)果才是1,否則為0
10 = 00000000 00000000 00000000 00001010
12 = 00000000 00000000 00000000 00001100
&
8 = 00000000 00000000 00000000 00001000
3. | 按位取或
兩個(gè)操作數(shù)任意一位為1結(jié)果就是1
10 = 00000000 00000000 00000000 00001010
12 = 00000000 00000000 00000000 00001100
|
14 = 00000000 00000000 00000000 00001110
4. ^ 按位異或
兩個(gè)操作數(shù)不同為1,相同為0
10 = 00000000 00000000 00000000 00001010
12 = 00000000 00000000 00000000 00001100
^
14 = 00000000 00000000 00000000 00000110
5. 用法:掩碼
掩碼是通過&(位與)將某些位設(shè)置為開(1),將某些位設(shè)置為關(guān)(0)。將掩碼0看做不透明,將1看著透明。
EG:
如只顯示第二、三位
107 = 0110 1011
6 = 0000 0110
&
2 = 0000 0010
6. 用法:打開位
打開位是通過 |(位或)打開一個(gè)值的特定位,同時(shí)保持其他位的不變。這是因?yàn)楹?位或都為0,和1位或都為1。
EG:
如只打開第二、三位
107 = 0110 1011
6 = 0000 0110
|
111 = 0110 1111
7. 用法:關(guān)閉位
關(guān)閉某些位
EG:
如關(guān)閉第二、三位
107 = 0110 1011
6 = 0000 0110
& ~
105 = 0110 1001
8. 用法:轉(zhuǎn)置位
如果一位為1則轉(zhuǎn)置為0,如果一位為1則轉(zhuǎn)置為0
EG:
如轉(zhuǎn)置第二、三位
107 = 0110 1011
6 = 0000 0110
^
105 = 0110 1101
二. 移位運(yùn)算符
- << 左移
左移運(yùn)算符是把操作數(shù)的值的每一位向左移動(dòng),移動(dòng)的位數(shù)有右邊的操作數(shù)決定,右側(cè)空出的位數(shù)用0填充
EG:
如轉(zhuǎn)置第二、三位
107 = 0110 1011 <<2
<<
172 = 1010 1100
在計(jì)算機(jī)中由于是32位的
107 = 0000 0000 0000 0000 0000 0000 0110 1011 <<2
<<
428 = 0000 0000 0000 0000 0000 0001 1010 1100
- >> 右移
右移運(yùn)算符是把操作數(shù)的值的每一位向右移動(dòng),移動(dòng)的位數(shù)有右邊的操作數(shù)決定,左邊丟棄的位數(shù)用0填充
EG:
如轉(zhuǎn)置第二、三位
107 = 0110 1011 >>2
>>
26 = 0001 1010
一、傳統(tǒng)的C方式位操作:
1.基本操作:
使用一個(gè)unsigned int變量來作為位容器。
2.操作符:
| 按位或操作符:result=exp1|exp2;當(dāng)exp1和exp2中對(duì)應(yīng)位中至少有一個(gè)為1時(shí),result中對(duì)應(yīng)位為1,否則為0。
& 按位與操作符::result=exp1&exp2;當(dāng)exp1和exp2中對(duì)應(yīng)位全為1時(shí),result中對(duì)應(yīng)位為1,否則為0。
^ 按位異或或操作符:result=exp1^exp2;當(dāng)exp1和exp2中對(duì)應(yīng)位不相同時(shí),result中對(duì)應(yīng)位為1,否則為0。
~ 反轉(zhuǎn)操作符:將位容器中的所有位都反轉(zhuǎn),1變?yōu)?,0變?yōu)?。
<< 按位左移操作符:exp<<n,將容器中所有的位向左移n位,空出的位用0填充。
>> 按位右移操作符:exp>>n,將容器中所有的位向右移n位,空出的位用0填充。
|=,&=,^= 分別對(duì)應(yīng)|&^三種操作符的復(fù)合操作符。
3.常用操作
這里我們假設(shè)有一個(gè)result的unsigned int變量用來儲(chǔ)存32個(gè)學(xué)生的成績(jī)(通過和不通過分別用0和1),這樣result就有33位(result從右至左,從0開始計(jì)算位數(shù),在這個(gè)例子中0位被浪費(fèi))。
(a) 將第27位設(shè)置為及格(設(shè)作1)其他位不變:
result|=(1<<27) //任意的位值與1作按位或操作其值為1,而與0作按位與操作其值不變
(b) 將第27位設(shè)置成不及格(設(shè)為0)。
result&=~(1<<27) //任意的位值與0作按位與操作其值為0,而與1作按位與操作其值不變
(c) 反轉(zhuǎn)第27位的值。
result^=(1<<27) //任意的位值與1作按位異或操作其值為1,而與0作按位異與操作其值不變
二、C++中的bitset容器
1.頭文件:
#include <bitset>
2.聲明一個(gè)容器:
(a)聲明一個(gè)指定位數(shù)的空容器(所有位設(shè)為0): bitset<int> bits;
(b)聲明一個(gè)指定位數(shù)并將指定的幾個(gè)位初始化為相應(yīng)值的容器: bitset<n> bits(int);
bitdet<int> bits(string&)
總結(jié):bitset模板類中類型參數(shù)傳遞容器的位數(shù),而構(gòu)造函數(shù)參數(shù)通過一個(gè)int或一個(gè)string&值來從右至左初始化容器中的相應(yīng)值。
3.bitset的基本用法:
操作 |
功能 |
用法 |
test(pos) |
pos位是否為1? |
a.test(4) |
any() |
任意位是否為1? |
a.any() |
none() |
是否沒有位為1? |
a.none() |
count() |
值是1的位的小數(shù) |
count() |
size() |
位元素的個(gè)數(shù) |
size() |
[pos] |
訪問pos位 |
a[4] |
flip() |
翻轉(zhuǎn)所有位 |
a.flip() |
flip(pos) |
翻轉(zhuǎn)pos位 |
a.flip(4) |
set() |
將所有位置1 |
a.set() |
set(pos) |
將pos位置1 |
a.set(4) |
reset() |
將所有位置0 |
a.reset() |
reset(pos) |
將pos位置0 |
a.reset(4) |
4.bitset與傳統(tǒng)C位操作及字符串的轉(zhuǎn)換
可以通過to_string()成員將容器轉(zhuǎn)輸出為一個(gè)string字符串,另外還可以用to_long()成員將容器輸出到傳統(tǒng)的用于C風(fēng)格的位容器中。如:
unsigned long bits = bits.to_long();
sting str(bits.to_string());
以上這篇深入理解C++移位運(yùn)算符就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持我們。
上一篇:C語(yǔ)言中實(shí)現(xiàn)KMP算法的實(shí)例講解
欄 目:C語(yǔ)言
本文標(biāo)題:深入理解C++移位運(yùn)算符
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/2259.html
您可能感興趣的文章
- 04-02c語(yǔ)言沒有round函數(shù) round c語(yǔ)言
- 01-10深入理解約瑟夫環(huán)的數(shù)學(xué)優(yōu)化方法
- 01-10深入二叉樹兩個(gè)結(jié)點(diǎn)的最低共同父結(jié)點(diǎn)的詳解
- 01-10深入理解C++中常見的關(guān)鍵字含義
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10深入Main函數(shù)中的參數(shù)argc,argv的使用詳解
- 01-10深入第K大數(shù)問題以及算法概要的詳解
- 01-10深入解析最長(zhǎng)公共子串
- 01-10c++中inline的用法分析
- 01-10深入理解鏈表的各類操作詳解


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 04-02jquery與jsp,用jquery
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-10delphi制作wav文件的方法
- 01-10C#中split用法實(shí)例總結(jié)
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什