首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言移位運算深度解析:算術右移 vs 邏輯右移、左移溢出、符號位保留

     編輯:關於C語言

一個 Bug 引發的思考


int value = -128;           // 0xFFFFFF80
int shift = INT_MIN;        // 0x80000000 (最高位 1)
for (int i=0; i<32; ++i) {
    putchar((shift & value) ? '1' : '0');
    shift >>= 1;            // 預期邏輯右移,實際算術右移!
}
// 輸出全 1,因為 shift 始終保持符號位 1

修正:unsigned int shift = 1u << 31; → 邏輯右移,高位補 0,正常輸出。

核心規則總結

操作有符號類型無符號類型
左移 <<邏輯左移(低位補 0),**可能溢出**改變符號位邏輯左移,溢出定義為模 2^n
右移 >>實現定義:幾乎所有編譯器採用算術右移(符號位複製)邏輯右移(高位補 0)

為什麼左移不分算術/邏輯?

  • 左移相當於 ×2,溢出時符號位**必然改變**(正變負、負變正)。
  • 保留原符號位毫無意義——結果已超出表示範圍,屬於未定義行為(有符號溢出 UB)。
  • 標準規定:有符號左移溢出 = UB;無符號左移 = 模 2^n 環繞。

為什麼右移要分兩種?

  • 右移相當於 ÷2(向零取整或向下取整)。
  • 有符號數若邏輯右移,負數會變成大正數(符號位變 0),語義錯誤。
  • 算術右移保持符號,負數除以 2 仍為負,符合數學直覺。
  • C 標準將有符號右移定為**實現定義**,但主流編譯器(GCC/MSVC/Clang)全做算術右移。

實戰最佳實踐

  1. 位操作統一用無符號類型:uint32_t, uint64_t,避免符號位驚喜。
  2. 有符號除以 2 的冪:優先用 /,編譯器會優化為算術右移,語義清晰且無 UB。
  3. 需邏輯右移有符號數:轉型 (uint32_t)x >> n。
  4. 檢查負數符號位:(x >> 31) & 1(假設 32-bit int)或 x < 0。

擴展:循環移位


// 循環左移 (ROL)
#define ROL32(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
// 循環右移 (ROR)
#define ROR32(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
// 注意:n=0 時移位寬度等於位寬屬 UB,實務加判斷

總結

移位運算看似簡單,符號性卻暗藏玄機。**「位操作用無符號,算術用有符號」**是鐵律。理解算術右移保號、左移溢出 UB、標準對右移實現定義的妥協,能避免 90% 的移位相關 Bug。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved