C++有符號(hào)和無(wú)符號(hào)整數(shù)的位移操作過程
在C++中,位移操作(左移<<和右移>>)是對(duì)整數(shù)二進(jìn)制位的直接操作,但其行為在有符號(hào)整數(shù)(signed) 和無(wú)符號(hào)整數(shù)(unsigned) 中存在顯著差異。
這種差異源于計(jì)算機(jī)對(duì)整數(shù)的存儲(chǔ)方式(補(bǔ)碼)和語(yǔ)言標(biāo)準(zhǔn)對(duì)操作的規(guī)定,理解這些差異是編寫正確位運(yùn)算代碼的關(guān)鍵。
一、位移操作的基本概念
位移操作的本質(zhì)是將整數(shù)的二進(jìn)制位向指定方向(左或右)移動(dòng)指定的位數(shù),空出的位由特定規(guī)則填充。在C++中,位移操作符的語(yǔ)法為:
- 左移:
a << n表示將a的二進(jìn)制位向左移動(dòng)n位,右側(cè)空出的位補(bǔ)0; - 右移:
a >> n表示將a的二進(jìn)制位向右移動(dòng)n位,左側(cè)空出的位填充規(guī)則因整數(shù)是否有符號(hào)而不同。
需要注意的是:
- 位移的位數(shù)
n必須是非負(fù)整數(shù),且不能大于等于操作數(shù)的位數(shù)(如32位整數(shù)位移32位及以上屬于未定義行為); - 位移操作的結(jié)果類型與左操作數(shù)的類型一致(如
int位移后仍為int)。
二、無(wú)符號(hào)整數(shù)(unsigned)的位移:邏輯移位
無(wú)符號(hào)整數(shù)的位移是邏輯移位(Logical Shift),即不考慮符號(hào)位,僅根據(jù)“空位補(bǔ)0”的規(guī)則處理,行為在C++標(biāo)準(zhǔn)中是完全明確的。
1. 無(wú)符號(hào)左移(unsigned << n)
左移時(shí),二進(jìn)制位整體向左移動(dòng)n位,右側(cè)空出的n位全部補(bǔ)0,左側(cè)超出類型位數(shù)的高位被直接丟棄。
舉例:假設(shè)unsigned int為32位,分析unsigned int a = 0x0000000F(二進(jìn)制00000000 00000000 00000000 00001111)的左移:
a << 1:左移1位后,右側(cè)補(bǔ)0,結(jié)果為0x0000001E(二進(jìn)制00000000 00000000 00000000 00011110);a << 4:左移4位后,右側(cè)補(bǔ)4個(gè)0,結(jié)果為0x000000F0(二進(jìn)制00000000 00000000 00000000 11110000);a << 28:左移28位后,高位超出32位的部分被丟棄,結(jié)果為0xF0000000(二進(jìn)制11110000 00000000 00000000 00000000)。
規(guī)律:無(wú)符號(hào)左移n位等價(jià)于“a * 2^n”(若結(jié)果未超出類型范圍)。
2. 無(wú)符號(hào)右移(unsigned >> n)
右移時(shí),二進(jìn)制位整體向右移動(dòng)n位,左側(cè)空出的n位全部補(bǔ)0,右側(cè)超出的n位被直接丟棄。
舉例:仍以32位unsigned int a = 0xF0000000(二進(jìn)制11110000 00000000 00000000 00000000)為例:
a >> 1:右移1位后,左側(cè)補(bǔ)0,結(jié)果為0x78000000(二進(jìn)制01111000 00000000 00000000 00000000);a >> 4:右移4位后,左側(cè)補(bǔ)4個(gè)0,結(jié)果為0x0F000000(二進(jìn)制00001111 00000000 00000000 00000000);a >> 28:右移28位后,左側(cè)補(bǔ)28個(gè)0,結(jié)果為0x0000000F(二進(jìn)制00000000 00000000 00000000 00001111)。
規(guī)律:無(wú)符號(hào)右移n位等價(jià)于“a / 2^n”(向下取整)。
三、有符號(hào)整數(shù)(signed)的位移:算術(shù)移位為主
有符號(hào)整數(shù)(如int、long long)在內(nèi)存中以補(bǔ)碼形式存儲(chǔ)(最高位為符號(hào)位:0表示正數(shù),1表示負(fù)數(shù))。其位移行為與無(wú)符號(hào)不同,尤其是右移,C++標(biāo)準(zhǔn)將其定義為“實(shí)現(xiàn)定義”(implementation-defined),但主流編譯器(如GCC、Clang、MSVC)均采用算術(shù)移位(Arithmetic Shift)規(guī)則。
1. 有符號(hào)左移(signed << n)
有符號(hào)左移的行為與無(wú)符號(hào)左移基本一致:二進(jìn)制位向左移動(dòng)n位,右側(cè)空出的n位補(bǔ)0,左側(cè)超出類型位數(shù)的高位(包括符號(hào)位)被丟棄。
注意:若左移后符號(hào)位發(fā)生變化(如正數(shù)左移后符號(hào)位變?yōu)?),結(jié)果可能超出該類型能表示的范圍,此時(shí)屬于未定義行為(undefined behavior)。
舉例:32位int(范圍-2^31 ~ 2^31-1):
- 正數(shù)左移:
int a = 0x0000000F(15),a << 1結(jié)果為0x0000001E(30),符號(hào)位仍為0(合法); - 負(fù)數(shù)左移:
int b = -0x0000000F(-15,補(bǔ)碼0xFFFFFFF1),b << 1結(jié)果為0xFFFFFFE2(-30),符號(hào)位仍為1(合法); - 未定義行為:
int c = 0x40000000(1073741824),c << 1結(jié)果為0x80000000(-2147483648),符號(hào)位從0變?yōu)?,屬于未定義行為(不同編譯器可能有差異)。
2. 有符號(hào)右移(signed >> n)
有符號(hào)右移是最容易產(chǎn)生差異的操作。主流編譯器采用算術(shù)移位:右側(cè)超出的n位被丟棄,左側(cè)空出的n位補(bǔ)符號(hào)位(正數(shù)補(bǔ)0,負(fù)數(shù)補(bǔ)1)。
這種規(guī)則的目的是保持位移后數(shù)值的“符號(hào)一致性”,尤其對(duì)負(fù)數(shù)而言,右移后仍為負(fù)數(shù)。
舉例1:正數(shù)右移int a = 0x0000000F(15,二進(jìn)制00000000 00000000 00000000 00001111):
- 符號(hào)位為0,右移時(shí)左側(cè)補(bǔ)0;
a >> 1:結(jié)果為0x00000007(7,二進(jìn)制00000000 00000000 00000000 00000111);a >> 4:結(jié)果為0x00000000(0,二進(jìn)制00000000 00000000 00000000 00000000)。
舉例2:負(fù)數(shù)右移int b = -0x0000000F(-15,補(bǔ)碼0xFFFFFFF1,二進(jìn)制11111111 11111111 11111111 11110001):
- 符號(hào)位為1,右移時(shí)左側(cè)補(bǔ)1;
b >> 1:右移1位后,左側(cè)補(bǔ)1,結(jié)果為0xFFFFFFF8(-8,二進(jìn)制11111111 11111111 11111111 11111000);b >> 4:右移4位后,左側(cè)補(bǔ)4個(gè)1,結(jié)果為0xFFFFFFF0(-16,二進(jìn)制11111111 11111111 11111111 11110000)。
規(guī)律:有符號(hào)右移n位對(duì)正數(shù)等價(jià)于“a / 2^n”(向零舍入);對(duì)負(fù)數(shù)等價(jià)于“a / 2^n”(向下取整,如-15 >> 1 = -8,而-15 / 2 = -7)。
四、有符號(hào)與無(wú)符號(hào)位移的核心差異對(duì)比
| 操作類型 | 無(wú)符號(hào)整數(shù)(unsigned) | 有符號(hào)整數(shù)(signed) |
|---|---|---|
| 左移(<<) | 邏輯移位,右側(cè)補(bǔ)0,高位丟棄 | 邏輯移位(同無(wú)符號(hào)),但可能觸發(fā)未定義行為 |
| 右移(>>) | =邏輯移位,左側(cè)補(bǔ)0,低位丟棄 | 算術(shù)移位(主流編譯器),左側(cè)補(bǔ)符號(hào)位 |
| 符號(hào)影響 | 無(wú)符號(hào)位,位移后仍為非負(fù) | 符號(hào)位不變(算術(shù)移位),負(fù)數(shù)位移后仍為負(fù) |
| 應(yīng)用場(chǎng)景 | 位運(yùn)算(如哈希、編碼) | 帶符號(hào)的數(shù)值計(jì)算(如除法近似) |
五、實(shí)際開發(fā)中的注意事項(xiàng)
1.避免對(duì)有符號(hào)整數(shù)進(jìn)行右移依賴
由于有符號(hào)右移是“實(shí)現(xiàn)定義”,若代碼需要跨編譯器兼容,應(yīng)避免依賴其行為。如需邏輯右移,可先轉(zhuǎn)換為無(wú)符號(hào)類型(如(unsigned int)a >> n)。
2.負(fù)數(shù)轉(zhuǎn)換為無(wú)符號(hào)的妙用
如之前的toHex函數(shù)中,負(fù)數(shù)轉(zhuǎn)換為unsigned int后,右移變?yōu)檫壿嬕莆唬ǜ呶谎a(bǔ)0),可正常處理補(bǔ)碼的所有位,避免無(wú)限循環(huán)(若用有符號(hào)右移,負(fù)數(shù)會(huì)因補(bǔ)1而永遠(yuǎn)不為0)。
3.警惕位移后的未定義行為
- 位移位數(shù)為負(fù)或大于等于類型位數(shù)(如32位
int移32位); - 有符號(hào)左移后符號(hào)位改變(超出表示范圍)。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
一些語(yǔ)言的按行讀取文件的代碼實(shí)現(xiàn)小結(jié)
這篇文章主要介紹了一些語(yǔ)言的按行讀取文件的代碼實(shí)現(xiàn)小結(jié),這里羅列了Java和C語(yǔ)言和C++以及PHP的實(shí)現(xiàn)需要的朋友可以參考下2015-08-08
windows下vscode環(huán)境c++利用matplotlibcpp繪圖
本文主要介紹了windows下vscode環(huán)境c++利用matplotlibcpp繪圖,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
使用pthread庫(kù)實(shí)現(xiàn)openssl多線程ssl服務(wù)端和客戶端
使用pthread庫(kù)實(shí)現(xiàn)openssl多線程ssl服務(wù)端和客戶端,大家參考使用吧2014-01-01
C++中用棧來(lái)判斷括號(hào)字符串匹配問題的實(shí)現(xiàn)方法
這篇文章主要介紹了C++中用棧來(lái)判斷括號(hào)字符串匹配問題的實(shí)現(xiàn)方法,是一個(gè)比較實(shí)用的算法技巧,包含了關(guān)于棧的基本操作,需要的朋友可以參考下2014-08-08
C程序?qū)崿F(xiàn)整數(shù)的素?cái)?shù)和分解問題
這篇文章主要介紹了C程序?qū)崿F(xiàn)整數(shù)的素?cái)?shù)和分解問題,對(duì)于算法的學(xué)習(xí)有不錯(cuò)的借鑒價(jià)值,需要的朋友可以參考下2014-09-09

