說明:本文主要摘錄自《深入理解計算機系統》第二章信息的表示與處理。
C語言還提供了一組移位運算,以便向左或者向右移動位模式。對於一個位表示為[xn-1,xn-2,…,x0]的操作數x,C表達式x<<k會生成一個值,其位表示為[xn-k-1,xn-k-2,…,x0,0,…,0]。也就是說,x向左移動k位,丟棄最高的k位,並在右端補k個0。移位量應該是一個0~n-1之間的值。移位運算是從左至右可結合的,所以x<<j<<k等價於(x<<j)<<k。
有一個相應的右移運算x>>k,但是它的行為有點微妙。一般而言,機器支持兩種形式的右移:邏輯右移和算術右移。邏輯右移在左端補k個0,得到的結果是[0,…,0,xn-1,xn-2,…,xk]。算術右移是在左端補k個最高有效位的值,得到的結果是[xn-1,…,xn-1,xn-1,xn-2,…,xk]。這種做法看上去可能有點奇特,但是我們會發現它對有符號整數數據的運算非常有用。
讓我們來看一個例子,下面的表給出了對某些實例8位數據做不同的移位操作得到的結果。
操作
值
參數X
[0110 0011] [1001 0101]
X<<4
[0011 0000] [0101 0000]
X>>4(邏輯右移)
[0000 0110] [0000 1001]
X>>4(算術右移)
[0000 0110] [1111 1001]
斜體的數字表示的是最右端(左移)或最左端(右移)填充的值。可以看到除了一個條目之外,其他的都涉及填充0。唯一的例外是算術右移[10010101]的情況。因為操作數的最高位是1,填充的值就是1。
C語言標准並沒有明確定義應該使用哪種類型的右移。對於無符號數據(也就是以限定詞unsigned聲明的整型對象),右移必須是邏輯的。而對於有符號數據(默認的聲明的整型對象),算術的或者邏輯的右移都可以。不幸的是,這就意味著任何假設一種或者另一種右移形式的代碼都潛在著可移植性問題。然而,實際上,幾乎所有的編譯器/機器組合都對有符號數據使用算術右移,且許多程序員也都假設機器會使用這種右移。
另一方面,Java對於如何進行右移有明確的定義。表達式x>>k會將x算術右移k個位置,而x>>>k會對x做邏輯右移。
對於一個由w位組成的數據類型,如果要移動k≥w位會得到什麼結果呢?例如,在一個32位機器上計算下面的表達式會得到什麼結果:
Int lval = 0xFEDCBA98 << 32;
int lval = 0xFEDCBA98 >> 36
unsigned lval = 0xFEDCBA98u >> 40;
C語言標准很小心地規避了說明在這種情況下該如何做。在許多機器上,當移動一個w位的值時,移位指令只考慮位移量的低log2w位,因此實際上位移量就是通過計算k mod w得到的。例如,在一台采用這個規則的32位機器上,上面三個移位運算分別是移動0、4和8位,得到結果:
Lval 0xFEDCBA98
Aval 0xFEDCBA9
Uval 0x00FEDCBA
不過這種行為對於C程序來說是沒有保證的,所以移位數量應該保持小於字長。另一方面,Java特別要求位移數量應該按照我們前面所講的求模的方法來計算。
常常有人會寫這樣的表達式1<<2+3<<4,其本意是(1<<2)+(3<<4)。但是在C語言中,前面的表達式等價於1<<(2+3)<<4,這是由於加法(和減法)的優先級比移位運算要高。然後,按照從左至右結合性規則,括號應該是這樣打的(1<<(2+3))<<4,因此得到的結果是512,而不是期望的52。在C表達式中搞錯優先級是一種常見的程序錯誤,而且常常很難檢查出來。所以當你拿不准的時候,請加上括號!
C++ Primer Plus 第6版 中文版 清晰有書簽PDF+源代碼 http://www.linuxidc.com/Linux/2014-05/101227.htm
讀C++ Primer 之構造函數陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm
讀C++ Primer 之智能指針 http://www.linuxidc.com/Linux/2011-08/40177.htm
讀C++ Primer 之句柄類 http://www.linuxidc.com/Linux/2011-08/40175.htm
將C語言梳理一下,分布在以下10個章節中: