許多初學(xué)者對(duì) 《《(左移)和 》》(右移)運(yùn)算符在 C/C++ 等編程語(yǔ)言中的工作方式感到困惑。在本專(zhuān)欄中,所有(嗯,相當(dāng)多)都將被揭示,但在我們滿(mǎn)懷熱情地投入戰(zhàn)斗之前,我們首先需要確保我們都了解一些基本概念。
位、半字節(jié)和字節(jié)
可以在計(jì)算機(jī)內(nèi)部存儲(chǔ)和操作的最小數(shù)據(jù)量是二進(jìn)制數(shù)字或位,它可用于存儲(chǔ)兩個(gè)不同的值:0 或 1。這些值在任何特定情況下實(shí)際體現(xiàn)的內(nèi)容時(shí)間取決于我們。例如,我們可能決定一位代表開(kāi)關(guān)的狀態(tài)(例如,向下或向上)或燈(例如,關(guān)閉或打開(kāi))或邏輯值(例如,假或真)?;蛘?,我們可能決定使用我們的位來(lái)表示數(shù)值 0(零)或 1(一)。
只是為了增加樂(lè)趣和輕浮性,我們可以隨時(shí)更改我們希望我們的位代表的內(nèi)容。在程序的一部分中,我們可以將位視為表示一個(gè)邏輯值;稍后,我們可能會(huì)決定將同一位視為體現(xiàn)一個(gè)數(shù)字量。電腦不在乎。它所看到的只是 0 或 1。它不知道我們?cè)谌魏翁囟〞r(shí)間使用 0 或 1 來(lái)表示什么。
我們只能用一個(gè)單獨(dú)的部分做很多事情。因此,計(jì)算機(jī)內(nèi)部的數(shù)據(jù)通常使用比特組進(jìn)行存儲(chǔ)和操作。常見(jiàn)的分組有 4 位、8 位、16 位、32 位和 64 位。一組 8 位稱(chēng)為byte,而一組 4 位稱(chēng)為nybble(或nibble)?!皟蓚€(gè) nybbles 組成一個(gè)字節(jié)”的想法是一個(gè)工程笑話,從而同時(shí)證明 (a) 工程師確實(shí)有幽默感和 (b) 他們的幽默不是很復(fù)雜。
已經(jīng)零星地嘗試采用其他大小的位組的術(shù)語(yǔ)。例如,tayste(或crumb)用于 2 位組;playte(或chawmp)用于 16 位組;32 位組的dynner(或gawble );和table用于 64 位組。但是到目前為止,您只能開(kāi)個(gè)玩笑,因此使用標(biāo)準(zhǔn)術(shù)語(yǔ)byte和nybble(或nibble)以外的任何內(nèi)容都極為罕見(jiàn)。
字節(jié)、字符和整數(shù)
在嘗試解釋與計(jì)算機(jī)相關(guān)的主題時(shí)遇到的問(wèn)題之一是,您經(jīng)常會(huì)陷入“雞或蛋”的境地,理想情況下,您需要理解概念 A 才能理解概念 B ,但是您確實(shí)需要熟悉概念 B 才能將您的大腦包裹在概念 A 上(有一個(gè)古老的編程笑話說(shuō):“要理解遞歸,必須先了解遞歸”)。
我們只是說(shuō),稍后我們將介紹無(wú)符號(hào)二進(jìn)制數(shù)的概念。稍后,我們將介紹有符號(hào)二進(jìn)制數(shù)的概念。關(guān)鍵是》》(右移)運(yùn)算符執(zhí)行其魔法的方式可能取決于我們是否告訴計(jì)算機(jī)將其正在操作的值視為有符號(hào)或無(wú)符號(hào)。
C/C++ 中兩種常用的數(shù)據(jù)類(lèi)型是 8 位char(字符)和int(整數(shù))。Arduino IDE/編譯器也支持 8-bit byte,但 ANSI-C 標(biāo)準(zhǔn)不支持這種類(lèi)型。在 Arduino 草圖中使用這些類(lèi)型的示例變量聲明如下:
byte myByte = 65;
char myChar = ‘A’;
int myInt = 65;
請(qǐng)注意,在 char 類(lèi)型的情況下,字符在計(jì)算機(jī)內(nèi)部使用ASCII 標(biāo)準(zhǔn)存儲(chǔ)為數(shù)字。在 ASCII 中,數(shù)字 65 代表大寫(xiě)“A”,因此“myChar = ‘A’;” 和“myChar = 65;” 兩者都會(huì)以包含數(shù)字 65 的變量 myChar 結(jié)束。
不幸的是,int 的大小是未定義的,并且因一臺(tái)計(jì)算機(jī)而異。例如,對(duì)于 Arduino,int 是 16 位寬,但在另一種類(lèi)型的計(jì)算機(jī)上可能是 16、32 或 64 位寬。
請(qǐng)記住,我們將在下面解釋有符號(hào)和無(wú)符號(hào)二進(jìn)制數(shù)之間的區(qū)別。然而,當(dāng)我們?cè)谶@里時(shí),我們應(yīng)該注意,一個(gè)字節(jié)將被 Arduino IDE 的編譯器視為未簽名,而一個(gè) int 將被視為由任何 C/C++ 編譯器簽名。只是為了咯咯笑和笑,C/C++ 標(biāo)準(zhǔn)允許將 char 類(lèi)型視為有符號(hào)或無(wú)符號(hào),具體取決于平臺(tái)和編譯器。
十進(jìn)制數(shù)和約定
十進(jìn)制(以 10 為底)數(shù)字系統(tǒng)由十位數(shù)字組成——0、1、2、3、4、5、6、7、8 和 9——并且是一個(gè)位值系統(tǒng)。這意味著十進(jìn)制數(shù)中的每一列都有一個(gè)與之關(guān)聯(lián)的“權(quán)重”,而一個(gè)數(shù)字的值取決于它所在的列。
如果我們?nèi)∫粋€(gè)像 362 這樣的數(shù)字,那么右邊的一列代表 1(個(gè)),左邊的下一列代表 10(十),下一列代表 100(百),依此類(lèi)推。 因此,當(dāng)我們看到 362 時(shí),我們將其理解為代表三個(gè)百、六個(gè)十和兩個(gè)一。
另外,當(dāng)我們用十進(jìn)制寫(xiě)一個(gè)數(shù)字時(shí),我們可能會(huì)在它后面加上一個(gè)符號(hào)來(lái)表示它是負(fù)數(shù)還是正數(shù);例如,–42 和 +42。按照慣例,沒(méi)有符號(hào)的數(shù)字(例如 42)被理解為正數(shù)。
無(wú)符號(hào)二進(jìn)制數(shù)
二進(jìn)制(以 2 為底)數(shù)系統(tǒng)僅包含兩個(gè)數(shù)字,0 和 1。讓我們考慮一個(gè)包含 0 和 1 的隨機(jī)模式的 8 位二進(jìn)制字段,例如 11001010。這種位模式的含義是什么我們決定它是。例如,每個(gè)位都可以表示現(xiàn)實(shí)世界中相關(guān)燈的邏輯狀態(tài),其中 0 表示關(guān)閉的燈,而 1 表示打開(kāi)的燈,反之亦然。
或者,我們可以使用我們的 8 位字段來(lái)表示一個(gè)數(shù)值。正如我們之前提到的,我們將在本專(zhuān)欄中考慮的兩種格式稱(chēng)為無(wú)符號(hào)和有符號(hào)二進(jìn)制數(shù)。讓我們從無(wú)符號(hào)品種開(kāi)始。顧名思義,我們知道無(wú)符號(hào)二進(jìn)制數(shù)沒(méi)有符號(hào),這意味著它們只能用于表示正值。
在無(wú)符號(hào)二進(jìn)制數(shù)的情況下,右列表示 1,下一列表示 2,下一列表示 4,下一列表示 8,依此類(lèi)推。還值得注意的是,在 8 位二進(jìn)制字段的情況下,我們將位編號(hào)從 0 到 7,其中位 0 稱(chēng)為最低有效位 (LSB),位 7 稱(chēng)為最高有效位位(MSB)。
因此,二進(jìn)制值 11001010 將等于 (1 × 128) + (1 × 64) + (0 × 32) + (0 × 16) + (1 × 8) + (0 × 4) + (1 × 2 ) + (0 × 1) = 202 十進(jìn)制。當(dāng)然,當(dāng)你習(xí)慣了這一點(diǎn)時(shí),你會(huì)跳過(guò)繁瑣的東西,簡(jiǎn)單地說(shuō):“二進(jìn)制的 11001010 等于 128 加 64 加 16 加 2 等于十進(jìn)制的 202?!?/p>
由于我們目前討論的是 8 位無(wú)符號(hào)二進(jìn)制數(shù),這意味著我們可以存儲(chǔ) 2 8 = 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 = 256 個(gè)不同的 0 和 1 模式,我們可以用于表示 0 到 255 范圍內(nèi)的正十進(jìn)制值。
請(qǐng)注意,沒(méi)有下標(biāo)的數(shù)字被假定為十進(jìn)制。當(dāng)引用其他基數(shù)中的數(shù)字時(shí),我們通常使用下標(biāo)來(lái)反映基數(shù)(例如,11001010 2表示二進(jìn)制/base-2 值),除非它在另一個(gè)基數(shù)中的事實(shí)從上下文中是顯而易見(jiàn)的,例如說(shuō)明它是文本中的二進(jìn)制值。
另請(qǐng)注意,在寫(xiě)入二進(jìn)制值時(shí),我們通常顯示前導(dǎo)零 (0) 以反映計(jì)算機(jī)內(nèi)關(guān)聯(lián)數(shù)據(jù)類(lèi)型或存儲(chǔ)位置的大小。例如,如果我們看到二進(jìn)制值 00001010,那么顯示四個(gè)前導(dǎo) 0 會(huì)立即通知我們正在使用 8 位值。
對(duì)無(wú)符號(hào)二進(jìn)制數(shù)使用 《《(左移)運(yùn)算符
正如我們之前所討論的,int類(lèi)型的大小是未定義的,并且因一臺(tái)計(jì)算機(jī)而異。它的unsigned int對(duì)應(yīng)物也是如此。因?yàn)檫@會(huì)導(dǎo)致問(wèn)題,現(xiàn)代 C/C++ 編譯器支持類(lèi)型uint8_t、uint16_t、uint32_t和uint64_t,它們?cè)试S我們分別聲明寬度正好為 8、16、32 和 64 位的無(wú)符號(hào)整數(shù)變量。例如:
uint8_t myUintA = B00011000;
uint8_t myUintB;
這聲明了兩個(gè)名為 myUintA 和 myUintB 的無(wú)符號(hào)整數(shù),它們的寬度正好是 8 位。此外,在 myUintA 的情況下,我們還為其分配了一個(gè) 8 位二進(jìn)制值 00011000,這等于十進(jìn)制的 (1 × 16) + (1 × 8) = 24(我們也可以使用“myUintA = 24 ;”以十進(jìn)制分配值,或“myUintA = 0×18;”以十六進(jìn)制分配值)。
現(xiàn)在假設(shè)我們執(zhí)行以下操作:
myUintB = myUintA 《《 1;
這將在 myUintA 中獲取我們?cè)瓉?lái)的 00011000 值,將其向左移動(dòng)一位,并將結(jié)果存儲(chǔ)在 myUintB 中。作為其中的一部分,它將一個(gè)新的 0 移到最右邊的列中。同時(shí),最左邊的位將“掉到最后”并被丟棄。。
當(dāng)然,所有這些動(dòng)作都是在計(jì)算機(jī)內(nèi)部同時(shí)進(jìn)行的。我們只是以這種方式將其拆分,以便我們更容易想象正在發(fā)生的事情。
觀察我們得到的二進(jìn)制值 00110000 等于十進(jìn)制的 (1 × 32) + (1 × 16) = 48。因?yàn)槲覀冊(cè)嫉亩M(jìn)制值 00011000 等于十進(jìn)制的 24,這意味著將其向左移動(dòng)一位與將其乘以 2 相同。
事實(shí)上,向左的每一個(gè)位移都等于乘以 2。例如,記住 myUintA 仍然包含 00011000,考慮當(dāng)我們執(zhí)行以下操作時(shí)會(huì)發(fā)生什么:
myUintB = myUintA 《《 2;
這將采用我們?cè)瓉?lái)的 00011000 值并將其向左移動(dòng)兩位。作為其中的一部分,它將兩個(gè)0 移到最右邊的列中,而最左邊的兩個(gè)位將“掉到最后”并被丟棄。再一次,我們可以將這個(gè)序列形象化如下:
在這種情況下,我們得到的二進(jìn)制值 01100000 等于十進(jìn)制的 (1 × 64) + (1 × 32) = 96。因?yàn)槲覀兊脑级M(jìn)制值 00011000 等于十進(jìn)制的 24,這意味著將其向左移動(dòng)兩位與將其乘以四(2 × 2 = 4)相同。
同樣,執(zhí)行“myUintB = myUintA 《《 3;”的操作 將我們的初始值 00011000 向左移動(dòng)三位,得到 11000000,相當(dāng)于十進(jìn)制的 192。這意味著將我們的原始值向左移動(dòng)三位與將其乘以八(2 × 2 × 2 = 8)相同。
當(dāng)然,當(dāng)我們開(kāi)始將 1 移到值的末尾時(shí),就會(huì)出現(xiàn)問(wèn)題。例如,“myUintB = myUintA 《《 4;” 會(huì)將我們的初始值 00011000 向左移動(dòng)四位,得到 10000000,相當(dāng)于十進(jìn)制的 128。雖然這是一個(gè)完全合法的操作,但我們必須知道 128 不等于 24 × 16。如果這對(duì)我們來(lái)說(shuō)是個(gè)問(wèn)題,那么解決方案是將 myUintA 和 myUintB 聲明為 uint16_t 或更大的類(lèi)型。
對(duì)無(wú)符號(hào)二進(jìn)制數(shù)使用 》》(右移)運(yùn)算符
假設(shè)我們像以前一樣聲明了 myUintA 和 myUintB 變量,但這一次,我們執(zhí)行以下操作:
myUintB = myUintA 》》 1;
這將采用我們?cè)瓉?lái)的 00011000 值并將其向右移動(dòng)一位。作為其中的一部分,它將一個(gè)新的 0 移到最左邊的列中。同時(shí),最右邊的位將“從末端脫落”并被丟棄。
在這種情況下,我們得到的二進(jìn)制值 00001100 等于十進(jìn)制的 (1 × 8) + (1 × 4) = 12。因?yàn)槲覀冏畛醯亩M(jìn)制值 00011000 等于十進(jìn)制的 24,這意味著將其向右移動(dòng)一位與將其除以 2 相同。
事實(shí)上,每一次右移就等于除以二。例如,使用“myUintB = myUintA 》》 2;”的操作 將我們的初始值 00011000 向右移動(dòng)兩位,得到 00000110,相當(dāng)于十進(jìn)制的 6。這意味著將我們的原始值向右移動(dòng)兩位與將其除以四相同。
同樣,使用“myUintB = myUintA 》》 3;” 將我們的初始值 00011000 向右移動(dòng)三位,得到 00000011,相當(dāng)于十進(jìn)制的 3。這意味著將我們的原始值向右移動(dòng)三位與將其除以八相同。
毫不奇怪,當(dāng)我們開(kāi)始將 1 移到末尾時(shí),就會(huì)出現(xiàn)問(wèn)題。例如,“myUintB = myUintA 》》 4;” 將我們的初始值 00011000 向右移動(dòng)四位,得到 00000001,相當(dāng)于十進(jìn)制的 1。再一次,雖然這是一個(gè)完全合法的操作,但我們必須知道 1 不等于 24 除以 16……或者是嗎?事實(shí)上,如果我們丟棄(截?cái)啵┤魏斡鄶?shù),24 除以 16 確實(shí)等于 1,這實(shí)際上就是我們?cè)谶@里所做的。
有符號(hào)二進(jìn)制數(shù)
在有符號(hào)二進(jìn)制數(shù)的情況下,我們使用 MSB 來(lái)表示數(shù)字的符號(hào)。其實(shí)比這復(fù)雜一點(diǎn),因?yàn)槲覀円灿眠@個(gè)位來(lái)表示一個(gè)量。
請(qǐng)注意,在這種情況下,第 7 位表示 –128s 列(與其無(wú)符號(hào)對(duì)應(yīng)項(xiàng)中的 +128s 列相反)。同時(shí),其余位繼續(xù)表示與以前相同的正值。
因此,二進(jìn)制值 00011000 仍然等于十進(jìn)制的 24;即 (0 × –128) + (0 × 64) + (0 × 32) + (1 × 16) + (1 × 8) + (0 × 4) + (0 × 2) + (0 × 1 ) = 24。但是,二進(jìn)制值 11001010 以前等于無(wú)符號(hào)形式的十進(jìn)制 202,現(xiàn)在等于 (1 x –128) + (1 × 64) + (0 × 32) + (0 × 16 ) + (1 × 8) + (0 × 4) + (1 × 2) + (0 × 1) = –128 + 74 = –54 十進(jìn)制。
和以前一樣,因?yàn)槲覀兡壳坝懻摰氖?8 位二進(jìn)制字段,所以我們可以存儲(chǔ) 2 8 = 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 = 256 個(gè)不同的 0 和 1 模式。在有符號(hào)二進(jìn)制數(shù)的情況下,我們可以使用這些模式來(lái)表示 –128 到 127 范圍內(nèi)的十進(jìn)制值。
這種表示形式稱(chēng)為二進(jìn)制補(bǔ)碼。雖然一開(kāi)始可能有點(diǎn)令人困惑,但這種格式在在計(jì)算機(jī)內(nèi)部創(chuàng)建算術(shù)邏輯函數(shù)方面提供了巨大的優(yōu)勢(shì)(我們將在我們的“從頭開(kāi)始構(gòu)建 4 位計(jì)算機(jī)”中更詳細(xì)地討論這些概念》系列文章)。
對(duì)有符號(hào)二進(jìn)制數(shù)使用《《(左移)運(yùn)算符int8_t、int16_t、int32_t和int64_t數(shù)據(jù)類(lèi)型允許我們分別聲明寬度正好為 8、16、32 和 64 位的有符號(hào)整數(shù)變量(Arduino int數(shù)據(jù)類(lèi)型等價(jià)于int16_t類(lèi)型)。
例如:
int8_t myIntA = B00011000;
int8_t myIntB;
這聲明了兩個(gè)名為 myIntA 和 myIntB 的有符號(hào)整數(shù),它們的寬度正好為 8 位。此外,在 myIntA 的情況下,我們還為其分配了一個(gè) 8 位二進(jìn)制值 00011000,它等于十進(jìn)制的 (1 × 16) + (1 × 8) = 24。
現(xiàn)在假設(shè)我們執(zhí)行以下操作:
myIntB = myIntA 《《 1;
和以前一樣,這將在 myIntA 中獲取我們?cè)瓉?lái)的 00011000 值,將其向左移動(dòng)一位,并將結(jié)果存儲(chǔ)在 myIntB 中。作為其中的一部分,它將一個(gè)新的 0 移到最右邊的列中。同時(shí),最左邊的位將“掉到最后”并被丟棄。我們可以將這個(gè)序列形象化如下:
再一次,所有這些動(dòng)作都在計(jì)算機(jī)內(nèi)部同時(shí)發(fā)生。我們只是以這種方式將其拆分,以便我們更容易想象正在發(fā)生的事情。再一次,我們得到的二進(jìn)制值 00110000 等于十進(jìn)制的 48。因?yàn)槲覀冊(cè)嫉亩M(jìn)制值 00011000 等于十進(jìn)制的 24,這意味著將其向左移動(dòng)一位與將其乘以 2 相同。
負(fù)數(shù)呢?假設(shè)我們存儲(chǔ)在 myIntA 中的原始二進(jìn)制值是 11100101,它等于 –128 + 64 + 32 + 4 + 1 = –27。如下圖所示,執(zhí)行“myIntB = myIntA 《《 1;”的操作 將我們的初始值 11100101 向左移動(dòng)一位,得到 11001010,這相當(dāng)于十進(jìn)制的 –128 + 64 + 8 + 2 = –54。
因?yàn)?–54 = –27 × 2,這意味著將帶負(fù)號(hào)的二進(jìn)制數(shù)左移一位與將其乘以 2 相同。
同樣,假設(shè)初始值為11100101,執(zhí)行“myUintB = myUintA 《《 2;”的操作 將產(chǎn)生 10010100,相當(dāng)于十進(jìn)制的 –108。這意味著將我們的原始值向左移動(dòng)兩位與將其乘以四相同。
在這種情況下,只有將符號(hào)位的值從 0 翻轉(zhuǎn)到 1 時(shí)才會(huì)開(kāi)始出現(xiàn)問(wèn)題,反之亦然。這包括任何中間“翻轉(zhuǎn)”;例如,將 10111111(十進(jìn)制的 –65)向左移動(dòng)兩位會(huì)得到 11111100(十進(jìn)制的 –4)。雖然符號(hào)位沒(méi)有改變(它仍然是 1),因?yàn)槲覀兛梢詫?0 想象為通過(guò)它,所以結(jié)果在數(shù)學(xué)上是不正確的,因?yàn)?–65 × 4 不會(huì)導(dǎo)致 –4。
需要注意的是,上述結(jié)果本身并不是無(wú)效的。計(jì)算機(jī)只是在做我們告訴它做的事情,我們告訴它使用的 8 位有符號(hào)二進(jìn)制數(shù)不足以容納結(jié)果,這不是可憐的小流氓的錯(cuò)。假設(shè)我們使用了 int16_t 數(shù)據(jù)類(lèi)型。在這種情況下,我們的起始值應(yīng)該是 1111111110111111,它仍然等于十進(jìn)制的 –65。將這個(gè) 16 位值向左移動(dòng)兩位得到 1111111011111100,相當(dāng)于 –260,這是我們期望看到的。
對(duì)有符號(hào)二進(jìn)制數(shù)使用 》》(右移)運(yùn)算符
這就是事情開(kāi)始變得有點(diǎn)棘手的地方,所以請(qǐng)坐起來(lái),深呼吸,并注意。早些時(shí)候,當(dāng)我們對(duì)無(wú)符號(hào)二進(jìn)制數(shù)執(zhí)行左移或右移操作時(shí),這些操作稱(chēng)為邏輯移位。在邏輯左移的情況下,我們將 0(零)移到 LSB;在邏輯右移的情況下,我們將 0 移入 MSB。
相比之下,當(dāng)我們對(duì)有符號(hào)二進(jìn)制數(shù)執(zhí)行左移或右移時(shí),這些被稱(chēng)為算術(shù)移位。在算術(shù)左移的情況下,我們將 0(零)移到 LSB,這意味著算術(shù)左移的工作方式與邏輯左移相同。當(dāng)我們執(zhí)行算術(shù)右移時(shí),棘手的部分就來(lái)了。在這種情況下,我們并不總是將 0 移入 MSB。相反,我們將原始符號(hào)位的副本轉(zhuǎn)移到 MSB 中。
讓我們從之前使用過(guò)的示例位模式開(kāi)始。假設(shè) myIntA 包含一個(gè)正符號(hào)二進(jìn)制值 00011000,相當(dāng)于十進(jìn)制的 24。觀察 MSB(最左邊的位,即符號(hào)位)為 0?,F(xiàn)在讓我們執(zhí)行操作“myintB = myIntA 》》 1;”。
正如預(yù)期的那樣,我們得到的二進(jìn)制值 00001100 等于十進(jìn)制的 (1 × 8) + (1 × 4) = 12。因?yàn)槲覀冏畛醯亩M(jìn)制值 00011000 等于十進(jìn)制的 24,這意味著將其向右移動(dòng)一位與將其除以 2 相同。
現(xiàn)在假設(shè)我們從包含負(fù)符號(hào)二進(jìn)制數(shù)的 myIntA 開(kāi)始,例如 10110000。觀察 MSB(最左邊的位,即符號(hào)位)為 1,因此該值等于 –128 + 32 + 16 = –80 十進(jìn)制?,F(xiàn)在讓我們執(zhí)行“myintB = myIntA 》》 1;”。
在這種情況下,因?yàn)槲覀儗⒃挤?hào)位的副本(即 1)移至 MSB,所以我們得到的二進(jìn)制值 11011000 等于十進(jìn)制的 –128 + 64 + 16 + 8 = –40。此外,因?yàn)槲覀冏畛醯亩M(jìn)制值 10110000 等于十進(jìn)制的 –80,這意味著將這個(gè)負(fù)值向右移動(dòng)一位,正如我們所期望的那樣,與將其除以 2 相同。
這里要注意的重要一點(diǎn)是,符號(hào)位將被復(fù)制到右移操作所需的盡可能多的位中。例如,如果我們從包含 10110000 的 myIntA 開(kāi)始并執(zhí)行“myintB = myIntA 》》 3;”操作。
在這種情況下,因?yàn)槲覀儗⒃挤?hào)位的副本(即 1)移到三個(gè) MSB 中,所以我們得到的二進(jìn)制值 11110110 等于十進(jìn)制的 –128 + 64 + 32 + 16 + 4 + 2 = –10。因?yàn)槲覀冏畛醯亩M(jìn)制值 10110000 等于十進(jìn)制的 –80,這意味著將這個(gè)負(fù)值向右移動(dòng)三位,正如我們所期望的那樣,與將其除以八(萬(wàn)歲)相同。
謹(jǐn)防!根據(jù)官方 C 標(biāo)準(zhǔn),右移有符號(hào)二進(jìn)制數(shù)會(huì)產(chǎn)生未定義的行為。上面描述的帶符號(hào)二進(jìn)制數(shù)右移的行為是大多數(shù)編譯器供應(yīng)商實(shí)現(xiàn)的方式,但不能保證!這就是為什么大多數(shù)標(biāo)準(zhǔn)(例如 MISRA-C)添加的規(guī)則本質(zhì)上是說(shuō)“位移有符號(hào)二進(jìn)制數(shù)是禁忌”,因?yàn)榧僭O(shè)符號(hào)將被保留,可以在一個(gè)編譯器上創(chuàng)建代碼,只是為了將您的代碼移動(dòng)到另一個(gè)編譯器以發(fā)現(xiàn)它不是。
審核編輯:郭婷
評(píng)論