回書目
第3章 使用masm
3.3 標號、變數和資料結構(4)
3.3.5變數的使用
1. 以不同的型別訪問變數
這個話題有點像c語言中的資料型別強制轉換,c語言中的型別轉換指的是把乙個變數的內容轉換成另外一種型別,轉換過程中,資料的內容已經發生了變化,如把浮點數轉換成整數後,小數點後的內容就丟失了。在masm中以不同的型別訪問不會對變數造成影響。
舉乙個簡單的例子,先以db方式定義乙個緩衝區:
szbuffer db 1024 dup (?)
然後從其他地方取得了資料,但資料的格式是以字方式組織的,要處理資料,最有效的方法是兩個位元組兩個位元組地處理,但如果在程式中把szbuffer的值放入ax:
mov ax,szbuffer
編譯器會報乙個錯:
error a2070: invalid instruction operands
意思是無效的指令操作,為什麼呢?因為szbuffer是用db定義的,而ax的尺寸是乙個word,等於兩個位元組,尺寸不符合。masm中,如果要用指定型別之外的長度訪問變數,必須顯式地指出要訪問的長度,這樣,編譯器忽略語法上的長度檢驗,僅使用變數的位址。使用的方法是:
型別ptr 變數名
型別可以是byte,word,dword,fword,qword,real8和real10。如:
mov ax,word ptr szbuffer
mov eax,dword ptr szbuffer
上述語句能通過編譯,當然,型別必須和操作的暫存器長度匹配。在這裡要注意的是,指定型別的引數訪問並不會去檢測長度是否溢位,看下面一段**:
.data
btest1 db 12h
wtest2 dw 1234h
dwtest3 dd 12345678h
….code
…mov al,btest1
mov ax,word ptr btest1
mov eax,dword ptr btest1
…上面的程式片斷,每一句執行後暫存器中的值是什麼呢,mov al,btest1這一句很顯然使al等於12h,下面的兩句呢,ax和eax難道等於0012h和00000012h嗎?實際執行結果很「奇怪」,竟然是3412h和78123412h,為什麼呢?先來看反彙編的內容:
;.data段中的變數
:00403000 12 34 12 78 56 34 12 ...
│ │ │
│ │ └─→dwtest3
│ └──────→wtest2
└─────────→btest1
;.code段中的**
:00401000 a000304000 mov al, byte ptr [00403000]
:00401005 66a100304000 mov ax, word ptr [00403000]
:0040100b a100304000 mov eax, dword ptr [00403000]
.data段中的變數是按順序從低位址往高位址排列的,對於超過乙個位元組的資料,80386處理器的資料排列方式是低位資料在低位址,所以wtest2的1234h在記憶體中的排列是34h 12h,因為34h是低位。同樣,dwtest3在記憶體中以78h 56h 34h 12h從低位址往高位址存放,在執行指令mov ax,word ptr btest1的時候,是從btest1的位址403000h處取乙個字,其長度已經超過了btest1的範圍並落到了wtest2中,從記憶體中看,是取了btest1的資料12h和wtest2的低位34h,在這兩個位元組中,12h位於低位址,所以ax中的數值是3412h。同樣道理,看另一條指令:
mov eax,dword ptr btest1
這條指令取了btest1,wtest2的全部和dwtest3的最低位78h,在記憶體中的排列是12h 34h 12h 78h,所以eax等於78123412h。
這個例子說明了彙編中用ptr強制覆蓋變數長度的時候,實質上是只用了變數的位址而禁止編譯器進行檢驗,編譯器並不會考慮定界的問題,程式設計師在使用的時候必須對記憶體中的資料排列有個全域性概念,以免越界訪問到意料之外的資料。
如果程式設計師的本意是類似於c語言的強制型別轉換,想把btest1的乙個位元組擴充套件到乙個字或乙個雙字再放到ax或eax中,高位保持0而不是越界訪問到其他的變數,可以用80386的擴充套件指令來實現。80386處理器提供的movzx指令可以實現這個功能,例如:
movzx ax,btest1 ;例1
movzx eax,btest1 ;例2
movzx eax,cl ;例3
movzx eax,ax ;例4
●例1把單位元組變數btest1的值擴充套件到16位放入ax中。
●例2把單位元組變數btest1的值擴充套件到32位放入eax中。
●例3把cl中的8位值擴充套件到32位放入eax中。
●例4把ax中的16位值擴充套件到32位放入eax中。
用movzx指令進行資料長度擴充套件是win32彙編中經常用到的技巧。
2. 變數的尺寸和數量
在源程式中用到變數的尺寸和數量的時候,可以用sizeof和lengthof偽指令來實現,格式是:
sizeof 變數名、資料型別或資料結構名
lengthof 變數名、資料型別或資料結構名
sizeof偽指令可以取得變數、資料型別或資料結構以位元組為單位的長度,lengthof可以取得變數中資料的項數。假如定義了以下資料:
stwndclass wndclass <>
szhello db 'hello,world!',0
dwtest dd 1,2,3,4
….code
…mov eax,sizeof stwndclass
mov ebx,sizeof wndclass
mov ecx,sizeof szhello
mov edx,sizeof dword
mov esi,sizeof dwtest
執行後eax的值是stwndclass結構的長度40,ebx同樣是40,ecx的值是13,就是「hello,world!」字串的長度加上乙個位元組的0結束符,edx的值是乙個雙字的長度:4,而esi則等於4個雙字的長度16。
如果把所有的sizeof換成lengthof,那麼eax會等於1,因為只定義了1項wndclass,而ecx同樣等於13,esi則等於4,而lengthof wndclass和lengthof dword是非法的用法,編譯程式會報錯。
要注意的是,sizeof和lengthof的數值是編譯時候產生的,由編譯器傳遞到指令中去,上邊的指令最後產生的**就是:
mov eax,40
mov ebx,40
mov ecx,13
mov edx,4
mov esi,16
如果為了把hello和world分兩行定義,szhello是這樣定義的:
szhello db 'hello',0dh,0ah
db 'world',0
那麼sizeof szhello是多少呢?注意!是7而不是13,masm中的變數定義只認一行,後一行db 'world',0實際上是另乙個沒有名稱的資料定義,編譯器認為sizeof szhello是第一行字元的數量。雖然把szhello的位址當引數傳給messagebox等函式顯示時會把兩行都顯示出來,但嚴格地說這是越界使用變數。雖然在實際的應用中這樣定義長字串的用法很普遍,因為如果要顯示一螢幕幫助,一行是不夠的,但要注意的是:要用到這種字串的長度時,千萬不要用sizeof去表示,最好是在程式中用lstrlen函式去計算。
回書目
演算法和資料結構 4 線性結構
程式 編譯可以通,未進行執行時測試。本程式測試線性邏輯結構 佇列 include include struct queuelist typedef struct queuelist queue typedef enum bool void createqueue queue queue,int si...
資料結構 (3 3) 棧
有關c 的部分在重讀c 的時候還會重來 棧是限制插入和刪除只能在乙個位置上進行的表,該位置是表的末端,叫做棧的頂 top 對棧的基本操作有push 進棧 和pop 出棧 前者相當於插入後者相當於刪除。最後插入的元素可以通過使用top例程在執行pop之前進行考察。對空棧進行的pop或top一般被認為是...
演算法和資料結構
演算法和資料結構 演算法和資料結構 千絲萬縷的聯絡 縱觀各種演算法書籍,大多都是將演算法和資料結構作為乙個整體來講述。資料結構就是陣列 樹結構等儲存或表現物件資料的結構。將演算法和資料結構作為整體講述,是因為必須依照演算法中的常用操作選擇資料結構。例如,事先將資料儲存在適當的樹形結構中,大多數情況下...