汇编语言数据类型以及数据定义详解
匯編器識別一組基本的內(nèi)部數(shù)據(jù)類型(intrinsic data type),按照數(shù)據(jù)大小(字節(jié)、字、雙字等等)、是否有符號、是整數(shù)還是實數(shù)來描述其類型。這些類型有相當(dāng)程度的重疊,例如,DWORD 類型(32 位,無符號整數(shù))就可以和 SDWORD 類型(32 位,有符號整數(shù))相互交換。
?
可能有人會說,程序員用 SDWORD 告訴讀程序的人,這個值是有符號的,但是,對于匯編器來說這不是強(qiáng)制性的。匯編器只評估操作數(shù)的大小。因此,舉例來說,程序員只能將 32 位整數(shù)指定為 DWORD、SDWORD 或者 REAL4 類型。
下表給出了全部內(nèi)部數(shù)據(jù)類型的列表,有些表項中的 IEEE 符號指的是 IEEE 計算機(jī)學(xué)會出版的標(biāo)準(zhǔn)實數(shù)格式。
| BYTE | 8 位無符號整數(shù),B 代表字節(jié) |
| SBYTE | 8 位有符號整數(shù),S 代表有符號 |
| WORD | 16 位無符號整數(shù) |
| SWORD | 16 位有符號整數(shù) |
| DWORD | 32 位無符號整數(shù),D 代表雙(字) |
| SDWORD | 32 位有符號整數(shù),SD 代表有符號雙(字) |
| FWORD | 48 位整數(shù)(保護(hù)模式中的遠(yuǎn)指針) |
| QWORD | 64 位整數(shù),Q 代表四(字) |
| TBYTE | 80 位(10 字節(jié))整數(shù),T 代表 10 字節(jié) |
| REAL4 | 32 位(4 字節(jié))IEEE 短實數(shù) |
| REAL8 | 64 位(8 字節(jié))IEEE 長實數(shù) |
| REAL10 | 80 位(10 字節(jié))IEEE 擴(kuò)展實數(shù) |
數(shù)據(jù)定義語句
數(shù)據(jù)定義語句(data definition statement)在內(nèi)存中為變量留岀存儲空間,并賦予一個可選的名字。數(shù)據(jù)定義語句根據(jù)內(nèi)部數(shù)據(jù)類型(上表)定義變量。
數(shù)據(jù)定義語法如下所示:
[name] directive initializer [,initializer]…
下面是數(shù)據(jù)定義語句的一個例子:
count DWORD 12345
其中:
- 名字:分配給變量的可選名字必須遵守標(biāo)識符規(guī)范。
- 偽指令:數(shù)據(jù)定義語句中的偽指令可以是 BYTE、WORD、DWORD、SBTYE、SWORD 或其他在上表中列出的類型。此外,它還可以是傳統(tǒng)數(shù)據(jù)定義偽指令,如下表所示。
?
| DB | 8位整數(shù) | DQ | 64 位整數(shù)或?qū)崝?shù) |
| DW | 16 位整數(shù) | DT | 定義 80 位(10 字節(jié))整數(shù) |
| DD | 32 位整數(shù)或?qū)崝?shù)? | ? | ? |
數(shù)據(jù)定義中至少要有一個初始值,即使該值為 0。其他初始值,如果有的話,用逗號分隔。對整數(shù)數(shù)據(jù)類型而言,初始值(initializer)是整數(shù)常量或是與變量類型,如 BYTE 或 WORD 相匹配的整數(shù)表達(dá)式。
如果程序員希望不對變量進(jìn)行初始化(隨機(jī)分配數(shù)值),可以用符號 ? 作為初始值。所有初始值,不論其格式,都由匯編器轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)。 初始值 0011 0010b、32h 和 50d 都具有相同的二進(jìn)制數(shù)值。
向 AddTwo 程序添加一個變量
前面《整數(shù)加減法》一節(jié)中介紹了 AddTwo 程序,現(xiàn)在創(chuàng)建它的一個新版本,并稱為 AddTwoSum。這個版本引入了變量 sum,它出現(xiàn)在完整的程序清單中:
;AddTowSum.asm .386 .model flat,stdcall .stack 4096 ExitProcess PROTO, dwExitCode:DWORD .data sum DWORD 0 .code main PROC mov eax,5 add eax,6 mov sum,eax INVOKE ExitProcess,0 main ENDP END main可以在第 13 行設(shè)置斷點,每次執(zhí)行一行,在調(diào)試器中單步執(zhí)行該程序。執(zhí)行完第 15 行后,將鼠標(biāo)懸停在變量 sum 上,查看其值?;蛘叽蜷_一個 Watch 窗口,打開過程如下:在 Debug 菜單中選擇 Windows(在調(diào)試會話中),選擇 Watch,并在四個可用選項(Watch1,Watch2,Watch3 或 Watch4)中選擇一個。然后,用鼠標(biāo)高亮顯示 sum 變量,將其拖拉到 Watch 窗口中。下圖展示了一個例子,其中用大箭頭指出了執(zhí)行第 15 行后,sum 的當(dāng)前值。
定義 BYTE 和 SBYTE 數(shù)據(jù)
BYTE(定義字節(jié))和 SBYTE(定義有符號字節(jié))為一個或多個無符號或有符號數(shù)值分配存儲空間。每個初始值在存儲時,都必須是 8 位的。例如:
value1 BYTE 'A' ;字符常量 value2 BYTE 0 ;最小無符號字節(jié) value3 BYTE 255 ;最大無符號字節(jié) value4 SBYTE -128 ;最小有符號字節(jié) value5 SBYTE +127 ;最大有符號字節(jié)問號(?)初始值使得變量未初始化,這意味著在運(yùn)行時分配數(shù)值到該變量:
value6 BYTE ?
可選名字是一個標(biāo)號,標(biāo)識從變量包含段的開始到該變量的偏移量。比如,如果 value1? 在數(shù)據(jù)段偏移量為 0000 處,并在內(nèi)存中占一個字節(jié),則 value2 就自動處于偏移量為 0001 處:
value1 BYTE 10h
value2 BYTE 20h
DB 偽指令也可以定義有符號或無符號的 8 位變量:
val1 DB 255? ? ;無符號字節(jié)
val2 DB -128? ;有符號字節(jié)
1) 多初始值
如果同一個數(shù)據(jù)定義中使用了多個初始值,那么它的標(biāo)號只指出第一個初始值的偏移量。在下面的例子中,假設(shè) list 的偏移量為 0000。那么,數(shù)值 10 的偏移量就為 0000, 20 的偏移量為 0001,30 的偏移量為 0002,40 的偏移量為 0003:
list BYTE 10,20,30,40
下圖給出了字節(jié)序列 list,顯示了每個字節(jié)及其偏移量。
并不是所有的數(shù)據(jù)定義都要用標(biāo)號。比如,在 list 后面繼續(xù)添加字節(jié)數(shù)組,就可以在下一行定義它們:
list BYTE 10,20,30,40 BYTE 50,60,70,80 BYTE 81,82,83,84在單個數(shù)據(jù)定義中,其初始值可以使用不同的基數(shù)。字符和字符串常量也可以自由組合。在下面的例子中,list1 和 list2 有相同的內(nèi)容:
list1 BYTE 10, 32, 41h, 00100010b list2 BYTE 0Ah, 20h, 'A', 22h2) 定義字符串
定義一個字符串,要用單引號或雙引號將其括起來。最常見的字符串類型是用一個空字節(jié)(值為0)作為結(jié)束標(biāo)記,稱為以空字節(jié)結(jié)束的字符串,很多編程語言中都使用這種類型的字符串:
greeting1 BYTE "Good afternoon",0 greeting2 BYTE 'Good night',0每個字符占一個字節(jié)的存儲空間。對于字節(jié)數(shù)值必須用逗號分隔的規(guī)則而言,字符串是一個例外。如果沒有這種例外,greeting1 就會被定義為:
greeting1 BYTE 'G', 'o', 'o', 'd'….etc.
這就顯得很冗長。一個字符串可以分為多行,并且不用為每一行都添加標(biāo)號:
greeting1 BYTE "Welcome to the Encryption Demo program " BYTE "created by Kip Irvine.",0dh, 0ah BYTE "If you wish to modify this program, please " BYTE "send me a copy.",0dh,0ah,0十六進(jìn)制代碼 0Dh 和 0Ah 也被稱為 CR/LF (回車換行符)或行結(jié)束字符。在編寫標(biāo)準(zhǔn)輸出時,它們將光標(biāo)移動到當(dāng)前行的下一行的左側(cè)。
行連續(xù)字符()把兩個源代碼行連接成一條語句,它必須是一行的最后一個字符。下面的語句是等價的:
greeting1 BYTE "Welcome to the Encryption Demo program "
和
greeting1?
BYTE "Welcome to the Encryption Demo program "
3) DUP 操作符
DUP 操作符使用一個整數(shù)表達(dá)式作為計數(shù)器,為多個數(shù)據(jù)項分配存儲空間。在為字符串或數(shù)組分配存儲空間時,這個操作符非常有用,它可以使用初始化或非初始化數(shù)據(jù):
BYTE 20 DUP ( 0 ) ;20 個字節(jié),值都為 0 BYTE 20 DUP ( ? ) ;20 個字節(jié),非初始化 BYTE 4 DUP ( "STACK" ) ; 20 個字節(jié):定義 WORD 和 SWORD 數(shù)據(jù)
WORD(定義字)和 SWORD(定義有符號字)偽指令為一個或多個 16 位整數(shù)分配存儲空間:
word1 WORD 65535 ;最大無符號數(shù) word2 SWORD -32768 ;最小有符號數(shù) word3 WORD ? ;未初始化,無符號也可以使用傳統(tǒng)的 DW 偽指令:
val1 DW 65535 ;無符號 val2 DW -32768 ;有符號16 位字?jǐn)?shù)組通過列舉元素或使用 DUP 操作符來創(chuàng)建字?jǐn)?shù)組。下面的數(shù)組包含了一組數(shù)值:
myList WORD 1,2,3,4,5
下圖是一個數(shù)組在內(nèi)存中的示意圖,假設(shè) myList 起始位置偏移量為0000。由于每個數(shù)值占兩個字節(jié),因此其地址遞增量為 2。
DUP 操作符提供了一種方便的方法來聲明數(shù)組:
array WORD 5 DUP (?) ; 5 個數(shù)值,未初始化
定義 DWORD 和 SDWORD 數(shù)據(jù)
DWORD(定義雙字)和 SDWORD(定義有符號雙字)偽指令為一個或多個 32 位整數(shù)分配存儲空間:
val1 DWORD 12345678h ;無符號 val2 SDWORD -2147483648 ;有符號 val3 DWORD 20 DUP (?) ;無符號數(shù)組傳統(tǒng)的 DD 偽指令也可以用來定義雙字?jǐn)?shù)據(jù):
val1 DD 12345678h ;無符號 val2 DD -2147483648 ;有符號DWORD 還可以用于聲明一種變量,這種變量包含的是另一個變量的 32 位偏移量。如下所示,pVal 包含的就是 val3 的偏移量:
pVal DWORD val3
32 位雙字?jǐn)?shù)組
現(xiàn)在定義一個雙字?jǐn)?shù)組,并顯式初始化它的每 一個值:
myList DWORD 1,2,3,4,5
下圖給岀了這個數(shù)組在內(nèi)存中的示意圖,假設(shè) myList 起始位置偏移量為 0000,偏移量增量為 4。
定義 QWORD 數(shù)據(jù)
QWORD(定義四字)偽指令為 64 位(8 字節(jié))數(shù)值分配存儲空間:
quad1 QWORD 1234567812345678h
傳統(tǒng)的 DQ 偽指令也可以用來定義四字?jǐn)?shù)據(jù):
quad1 DQ 1234567812345678h
定義壓縮 BCD(TBYTE)數(shù)據(jù)
Intel 把一個壓縮的二進(jìn)制編碼的十進(jìn)制(BCD, Binary Coded Decimal)整數(shù)存放在一個 10 字節(jié)的包中。每個字節(jié)(除了最高字節(jié)之外)包含兩個十進(jìn)制數(shù)字。在低 9 個存儲字節(jié)中,每半個字節(jié)都存放了一個十進(jìn)制數(shù)字。最高字節(jié)中,最高位表示該數(shù)的符號位。如果最高字節(jié)為 80h,該數(shù)就是負(fù)數(shù);如果最高字節(jié)為 00h,該數(shù)就是正數(shù)。整數(shù)的范圍是 -999 999 999 999 999 999 到 +999 999 999 999 999 999。
示例下表列出了正、負(fù)十進(jìn)制數(shù) 1234 的十六進(jìn)制存儲字節(jié),排列順序從最低有效字節(jié)到最高有效字節(jié):
| +1234 | 34 12 00 00 00 00 00 00 00 00 |
| -1234 | 34 12 00 00 00 00 00 00 00 80 |
MASM 使用 TBYTE 偽指令來定義壓縮 BCD 變量。常數(shù)初始值必須是十六進(jìn)制的,因為,匯編器不會自動將十進(jìn)制初始值轉(zhuǎn)換為 BCD 碼。下面的兩個例子展示了十進(jìn)制 數(shù) -1234 有效和無效的表達(dá)方式:
intVal TBYTE 800000000000001234h ;有效 intVal TBYTE -1234 ;無效第二個例子無效的原因是 MASM 將常數(shù)編碼為二進(jìn)制整數(shù),而不是壓縮 BCD 整數(shù)。
如果想要把一個實數(shù)編碼為壓縮 BCD 碼,可以先用 FLD 指令將該實數(shù)加載到浮點寄存器堆棧,再用 FBSTP 指令將其轉(zhuǎn)換為壓縮 BCD 碼,該指令會把數(shù)值舍入到最接近的整數(shù):
.data posVal REAL8 1.5 bcdVal TBYTE ? .code fid posVal ;加載到浮點堆棧 fbstp bcdVal ;向上舍入到 2,壓縮 BCD 碼值如果 posVal 等于 1.5,結(jié)果 BCD 值就是 2。
定義浮點類型
REAL4 定義 4 字節(jié)單精度浮點變量。REAL8 定義 8 字節(jié)雙精度數(shù)值,REAL10 定義 10 字節(jié)擴(kuò)展精度數(shù)值。每個偽指令都需要一個或多個實常數(shù)初始值:
rVal1 REAL4 -1.2 rVal2 REAL8 3.2E-260 rVal3 REAL10 4.6E+4096 ShortArray REAL4 20 DUP(0.0)下表描述了標(biāo)準(zhǔn)實類型的最少有效數(shù)字個數(shù)和近似范圍:
| 短實數(shù) | 6 | 1.18x 10-38?to 3.40 x 1038 |
| 長實數(shù) | 15 | 2.23 x 10-308?to 1.79 x 10308 |
| 擴(kuò)展精度實數(shù) | 19 | 3.37 x 10-4932?to 1.18 x 104932 |
DD、DQ 和 DT 偽指令也可以定義實數(shù):
rVal1 DD -1.2 ;短實數(shù) rVal2 DQ 3.2E-260 ;長實數(shù) rVal3 DT 4.6E+4096 ;擴(kuò)展精度實數(shù)MASM 匯編器包含了諸如 wal4 和 real8 的數(shù)據(jù)類型,這些類型表明數(shù)值是實數(shù)。更準(zhǔn)確地說,這些數(shù)值是浮點數(shù),其精度和范圍都是有限的。從數(shù)學(xué)的角度來看,實數(shù)的精度和大小是無限的。
變量加法程序
到目前為止,本節(jié)的示例程序?qū)崿F(xiàn)了存儲在寄存器中的整數(shù)加法?,F(xiàn)在已經(jīng)對如何定義數(shù)據(jù)有了一些了解,那么可以對同樣的程序進(jìn)行修改,使之實現(xiàn)三個整數(shù)變量相加,并將和數(shù)存放到第四個變量中。
;AddTowSum.asm .386 .model flat,stdcall .stack 4096 ExitProcess PROTO, dwExitCode:DWORD .data firstval DWORD 20002000h secondval DWORD 11111111h thirdval DWORD 22222222h sum DWORD 0 .code main PROC mov eax,firstval add eax,secondval add eax,thirdval mov sum,eax INVOKE ExitProcess,0 main ENDP END main注意,已經(jīng)用非零數(shù)值對三個變量進(jìn)行了初始化(9?11 行)。16?18 行進(jìn)行變量相加。x86 指令集不允許將一個變量直接與另一個變量相加,但是允許一個變量與一個寄存器相加。這就是為什么 16?17 行用 EAX 作累加器的原因:
mov eax,firstval
add eax,secondval
第 17 行之后,EAX 中包含了 firstval 和 secondval 之和。接著,第 18 行把 thirdval 加到 EAX 中的和數(shù)上:
add eax,thirdval
最后,在第 19 行,和數(shù)被復(fù)制到名稱為 sum 的變量中:
mov sum,eax
作為練習(xí),鼓勵大家在調(diào)試會話中運(yùn)行本程序,并在每條指令執(zhí)行后檢查每個寄存器。最終和數(shù)應(yīng)為十六進(jìn)制的 53335333。
在調(diào)試會話過程中,如果想要變量顯示為十六進(jìn)制,則按下述步驟操作:鼠標(biāo)在變量或寄存器上懸停 1 秒,直到一個灰色矩形框出現(xiàn)在鼠標(biāo)下。右鍵點擊該矩形框,在彈出菜單中選擇 Hexadecimal Display。
小端順序
x86 處理器在內(nèi)存中按小端(little-endian)順序(低到高)存放和檢索數(shù)據(jù)。最低有效字節(jié)存放在分配給該數(shù)據(jù)的第一個內(nèi)存地址中,剩余字節(jié)存放在隨后的連續(xù)內(nèi)存位置中。考慮一個雙字 12345678h。如果將其存放在偏移量為 0000 的位置,則 78h 存放在第一個字節(jié),56h 存放在第二個字節(jié),余下的字節(jié)存放地址偏移量為 0002 和 0003,如下圖所示。
其他有些計算機(jī)系統(tǒng)采用的是大端順序(高到低)。 下圖展示了 12345678h 從偏移量 0000 開始的大端順序存放。
聲明未初始化數(shù)據(jù)
.DATA ? 偽指令聲明未初始化數(shù)據(jù)。當(dāng)定義大量未初始化數(shù)據(jù)時,.DATA ? 偽指令減少了編譯程序的大小。例如,下述代碼是有效聲明:
.data smallArray DWORD 10 DUP (0) ;40 個字節(jié) .data? bigArray DWORD 5000 DUP ( ? ) ;20 000 個字節(jié),未初始化而另一方面,下述代碼生成的編譯程序?qū)鄬?20 000 個字節(jié):
.data smallArray DWORD 10 DUP ( 0 ) ; 40 個字節(jié) bigArray DWORD 5000 DUP ( ? ) ; 20 000 個字節(jié)代碼與數(shù)據(jù)混合匯編器允許在程序中進(jìn)行代碼和數(shù)據(jù)的來回切換。比如,想要聲明一個變量,使其只能在程序的局部區(qū)域中使用。下述示例在兩個代碼語句之間插入了一個名為 temp 的變量:
.code mov eax,ebx .data temp DWORD ? .code mov temp,eax盡管 temp 聲明的出現(xiàn)打斷了可執(zhí)行指令流,MASM 還是會把 temp 放在數(shù)據(jù)段中,并與保持編譯的代碼段分隔開。然而同時,混用 .code 和 .data 偽指令會使得程序變得難以閱讀。
下一篇:等號偽指令
強(qiáng)力推薦閱讀文章
年薪40+W的大數(shù)據(jù)開發(fā)【教程】,都在這兒!
總結(jié)
以上是生活随笔為你收集整理的汇编语言数据类型以及数据定义详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 汇编器以及汇编流程
- 下一篇: vsftpd的虚拟账户配置