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) |
uint32_t, uint64_t,避免符號位驚喜。/,編譯器會優化為算術右移,語義清晰且無 UB。(uint32_t)x >> n。(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。