在匯編語言中,所有數(shù)據(jù)都有一個(gè)指定的大小。為了與其它數(shù)據(jù)一起使用而改變數(shù)據(jù)大小是不常用的。減小它的大小是最簡(jiǎn)單的。
減小數(shù)據(jù)的大小
要減小數(shù)據(jù)的大小,只需要簡(jiǎn)單地將多余的有效位移位即可。這是一個(gè)普通的例子:
mov ax, 0034h ; ax = 52 (以十六位儲(chǔ)存)
mov cl, al ; cl = ax的低八位
當(dāng)然,如果數(shù)字不能以更小的大小來正確描述,那么減小數(shù)據(jù)的大小將不能工作。例如,如果AX是0134h (或十進(jìn)制的308) ,那么上面的代碼仍然將CL置為34h。這種方法對(duì)于有符號(hào)和無符號(hào)數(shù)都能工作。考慮有符號(hào)數(shù):如果AX是FFFFh (也就是?1),那么CL 將會(huì)是FFh (一個(gè)字節(jié)表示的?1)。然而,注意如果在AX里的值是無符號(hào)的,這個(gè)就不正確了!
無符號(hào)數(shù)的規(guī)則是:為了能轉(zhuǎn)換正確,所有需要移除的位都必須是0。有符號(hào)數(shù)的規(guī)則是:需要移除的位必須要么都是1,要么都是0。另外,沒有移除的第一個(gè)比特位的值必須等于移除的位的第一位。這一位將會(huì)是變小的值的新的符號(hào)位。這一位與原始符號(hào)位相同是非常重要的!
增大數(shù)據(jù)的大小
增大數(shù)據(jù)的大小比減小數(shù)據(jù)的大小更復(fù)雜??紤]十六進(jìn)制字節(jié):FF。如果它擴(kuò)展成一個(gè)字,那么這個(gè)字的值應(yīng)該是多少呢?它取決于如何解釋FF。如果FF是一個(gè)無符號(hào)字節(jié)(十進(jìn)制中為),那么這個(gè)字就應(yīng)該是00FF;但是,如果它是一個(gè)有符號(hào)字節(jié)(十進(jìn)制中為?1),那么這個(gè)字就應(yīng)該為FFFF。
一般說來,擴(kuò)展一個(gè)無符號(hào)數(shù),你需將所有的新位置為0.因此,F(xiàn)F就變成了00FF。但是,擴(kuò)展一個(gè)有符號(hào)數(shù),你必須擴(kuò)展符號(hào)位。這就意味著所有的新位通過復(fù)制符號(hào)位得到。因?yàn)镕F的符號(hào)位為1,所以新的位必須全為1,從而得到FFFF。如果有符號(hào)數(shù)5A (十進(jìn)制中為90)被擴(kuò)展了,那么結(jié)
果應(yīng)該是005A。
80386提供了好幾條指令用于數(shù)的擴(kuò)展。謹(jǐn)記電腦是不清楚一個(gè)數(shù)是有符號(hào)的或是無符號(hào)的。這取決于程序員是否用了正確的指令。對(duì)于無符號(hào)數(shù),你可以使用MOV指令簡(jiǎn)單地將高位置0。例如,將一個(gè)在AL中的無符號(hào)字節(jié)擴(kuò)展到AX中:
mov ah, 0 ; 輸出高8位為0
但是,使用MOV指令把一個(gè)在AX中的無符號(hào)字轉(zhuǎn)換成在EAX中的無符號(hào)雙字是不可能的。為什么不可以呢?因?yàn)樵贛OV指令中沒有方法指定EAX的高16位。80386通過提供一個(gè)新的指令MOVZX來解決這個(gè)問題。這個(gè)指令有兩個(gè)操作數(shù)。目的操作數(shù)(第一個(gè)操作數(shù))必須是一個(gè)16或32位的寄存器。源
操作數(shù)(第二個(gè)操作數(shù))可以是一個(gè)8或16位的寄存器或內(nèi)存中的一個(gè)字。另一個(gè)限制是目的操作數(shù)必須大于源操作數(shù)。(許多指令要求源和目的操作數(shù)必須是一樣的大小。) 這兒有幾個(gè)例子:
對(duì)于有符號(hào)數(shù),在任何情況下,沒有一個(gè)簡(jiǎn)單的方法來使用MOV 指令。8086提供了幾條用來擴(kuò)展有符號(hào)數(shù)的指令。CBW (Convert Byte toWord(字節(jié)轉(zhuǎn)換成字))指令將AL正負(fù)號(hào)擴(kuò)展成AX。操作數(shù)是不顯示的。CWD(ConvertWord to Double word(字轉(zhuǎn)換成雙字))指令將AX正負(fù)號(hào)擴(kuò)展成DX:AX。
DX:AX表示法表示將DX和AX寄存器當(dāng)作一個(gè)32位寄存器來看待,其中高16位在DX中,低16位在AX中。(記住8086沒有32位寄存器!) 80386加了好幾條新的指令。CWDE (Convert Word to Double word Extended(字轉(zhuǎn)換成擴(kuò)展的雙字))指令將AX正負(fù)號(hào)擴(kuò)展成EAX。CDQ (Convert Double word toQuad word(雙字?jǐn)U展成四字))指令將EAX正負(fù)號(hào)擴(kuò)展成EDX:EAX (64位!).最后,MOVSX 指令像MOVZX指令一樣工作,除了它使用有符號(hào)數(shù)的規(guī)則外。
C編程中的應(yīng)用
無符號(hào)和有符號(hào)數(shù)的擴(kuò)展同樣發(fā)生在C中。C中的變量可以被聲明成有符號(hào)的,或無符號(hào)的(int是有符號(hào)的)??紤]在圖2.1中的代碼。在第3行中,變量a使用了無符號(hào)數(shù)的規(guī)則(使用MOVZX)進(jìn)行了擴(kuò)展,但是在第4行,變量b使用了有符號(hào)數(shù)的規(guī)則(使用MOVSX)進(jìn)行了擴(kuò)展。
這有一個(gè)直接與這個(gè)主題相關(guān)的普遍的C編程中的一個(gè)bug??紤]在圖2.2中的代碼。fgetc()的原型是:
int fgetc( FILE * );
一個(gè)可能的問題:為什么這個(gè)函數(shù)返回一個(gè)int類型,然后又被當(dāng)作字符類型被讀呢?原因是它一般確實(shí)是返回一個(gè)char 類型的值(使用0擴(kuò)展成一個(gè)int類型的值)。但是,有一個(gè)值它可能不會(huì)以字符的形式返回:EOF。這是一個(gè)宏,通常被定義為?1。因此,fgetc()不是返回一個(gè)通過擴(kuò)展成int類型得到的char類型的值(在十六進(jìn)制中表示為000000xx),就是EOF(在十六進(jìn)制中表示為FFFFFFFF)。
圖2.2中的程序的基本的問題是fgetc()返回一個(gè)int類型,但是這個(gè)值以char的形式儲(chǔ)存。C將會(huì)切去較高順序的位來使int類型的值適合char類型。唯一的問題是數(shù)(十六進(jìn)制) 000000FF和FFFFFFFF都會(huì)被切成字節(jié)FF。因此,while循環(huán)不能區(qū)別從文件中讀到的字節(jié)FF和文件的結(jié)束。
實(shí)際上,在這種情況下代碼會(huì)怎么做,取決于char是有符號(hào)的,還是無符號(hào)的。為什么?因?yàn)樵诘?行ch是與EOF進(jìn)行比較。因?yàn)镋OF是一個(gè)int類型的值,ch將會(huì)擴(kuò)展成一個(gè)int類型,以便于這兩個(gè)值在相同大小下比較。就像圖2.1展示的一樣,變量是有符號(hào)的還是無符號(hào)的是非常重要的。
如果char是無符號(hào)的,那么FF就被擴(kuò)展成000000FF。這個(gè)拿去與EOF
(FFFFFFFF)比較,它們并不相等。因此,循環(huán)不會(huì)結(jié)束。
如果char是有符號(hào)的,F(xiàn)F就被擴(kuò)展成FFFFFFFF。這就導(dǎo)致比較相等,循環(huán)結(jié)束。但是,因?yàn)樽止?jié)FF可能是從文件中讀到的,循環(huán)就可能過早地被結(jié)束了。
這個(gè)問題的解決辦法是定義ch 變量為int類型,而不是char類型。當(dāng)做了這個(gè)改變,在第2行就不會(huì)有切去和擴(kuò)展操作執(zhí)行了。在循環(huán)休內(nèi),對(duì)值進(jìn)行切去操作是很安全的,因?yàn)閏h在這兒必須實(shí)際上已經(jīng)是一個(gè)簡(jiǎn)單的字節(jié)了。
更多建議: