先由一道題目引入:有兩個變量a和b,不用if、?:、switch等判斷語句,找出較大的那個變量。
其中一種答案如下:
char* result[] = {"a is larger", "b is larger"}; int c = a - b; c = unsigned(c) >> (sizeof(int) * 8 - 1); cout << result[c] << endl;
sizeof(int) * 8 很好理解,就是求出int型占內存的bit數,-1就是為了右移后保留最高位,即保留符號位。
問題來了:為什么移位時要把c轉為unsigned呢?而一旦去掉unsigned關鍵字的話運行出錯。
在vc2008中設置斷點,調試,進入反匯編,發現問題出現在 >> 時,如果c為-1,則EAX(32位寄存器) = 0xffffffff,沒有unsigned情況下移位后還是0xffffffff。
//c = c >> (sizeof(int) * 8 - 1); 004114F3 mov eax,dword ptr [c] 004114F6 sar eax,1Fh 004114F9 mov dword ptr [c],eax
而有unsigned的話移位后則為0x00000001。
//c = unsigned(c) >> (sizeof(int) * 8 - 1); 004114F3 mov eax,dword ptr [c] 004114F6 shr eax,1Fh 004114F9 mov dword ptr [c],eax
c右移的規則是:無符號數右移時左邊高位移入0;有符號數右移時:1.符號位為0,移入0;2.符號位為1,有的系統移入1(算術右移),有的系統移入0(邏輯右移)。
經驗證vc和gcc均默認為算術右移,上面的匯編代碼也說明了這一點,有unsigned時右移采用的是shr(shift logical right)邏輯右移,無unsigned時采用sar(shift arithmetic right)算術右移。
《c和指針》中也建議不要對有符號數進行>>操作,所以為了確保結果一定,>>時把數值轉成unsigned。
又試了一下<<,發現signed和unsigned都是采用shl邏輯左移,應該是shl和sal效果都一樣,所以編譯器就統一采用shl。
試驗int 0x80000000 << 1結果為0,說明符號位對左移沒影響。