WIN32汇编语言教程:第03章 使用MASM · 3.3 标号、变量和数据结构(1)
当程序中要跳转到另一位置时,需要有一个标识来指示新的位置,这就是标号,通过在目的地址的前面放上一个标号,可以在指令中使用标号来代替直接使用地址。
使用变量是任何编程语言都要遇到的工作,Win32汇编也不例外,在MASM中使用变量也有需要注意的几个问题,错误地使用变量定义或用错误的方法初始化变量会带来难以定位的错误。变量是计算机内存中已命名的存储位置,在C语言中有很多种类的变量,如整数型、浮点型和字符串等,不同的变量有不同的用途和尺寸,比如说虽然长整数和单精度浮点数都是32位长,但它们的用途不同。
顾名思义,变量的值在程序运行中是需要改变的,所以它必须定义在可写的段内,如 .data和 .data?,或者在堆栈内。按照定义的位置不同,MASM中的变量也分为全局变量和局部变量两种。
在MASM中标号和变量的命名规范是相同的,它们是:
1. 可以用字母、数字、下划线及符号@、$和?。
2. 第一个符号不能是数字。
3. 长度不能超过240个字符。
4. 不能使用指令名等关键字。
5. 在作用域内必须是惟一的。
3.3.1 标号
1. 标号的定义
当在程序中使用一条跳转指令的时候,可以用标号来表示跳转的目的地,编译器在编译的时候会把它替换成地址,标号既可以定义在目的指令同一行的头部,也可以在目的指令前一行单独用一行定义,标号定义的格式是:
标号名: 目的指令
标号的作用域是当前的子程序,在单个子程序中的标号不能同名,否则编译器不知该用哪个地址,但在不同的子程序中可以有相同名称的标号,这意味着不能从一个子程序中用跳转指令跳到另一个子程序中。
在低版本的MASM中,标号在整个程序中是惟一的,子程序中的标号也可以从整个程序的任何地方转入。但Win32汇编使用的高版本MASM中不允许这样,这是为了提供对局部变量和参数的支持,由于在子程序入口处有对堆栈的初始化指令,所以一个子程序不允许有多个入口,其结果就是标号的作用域变成了单个子程序范围。
2. MASM中的@@
在DOS时代,为标号起名是个麻烦的事情,因为汇编指令用到跳转指令特别多,任何比较和测试等都要涉及跳转,所以在程序中会有很多标号,在整个程序范围内起个不重名的标号要费一番功夫,结果常常用addr1和addr2之类的标号一直延续下去,如果后来要在中间插一个标号,那么就常常出现addr1_1和loop10_5之类奇怪的标号。
实际上,很多标号只会使用一到两次,而且不一定非要起个有意义的名称,如汇编程序中下列代码结构很多:
mov cx,1234h cmp flag,1 jz loc1 mov cx,1000h loc1: ... loop loc1
loc1在别的地方就再也用不到了,对于这种情况,高版本的MASM用@@标号去代替它:
mov cx,1234h cmp flag,1 jz @F mov cx,1000h @@: ... loop @B
当用@@做标号时,可以用@F和@B来引用它,@F表示本条指令后的第一个@@标号,@B表示本条指令前的第一个@@标号,程序中可以有多个@@标号,@B和@F只寻找匹配最近的一个。
不要在间隔太远的代码中使用@@标号,因为在以后的修改中@@和@B,@F中间可能会被无意中插入一个新的@@,这样一来,@B或@F就会引用到错误的地方去,源程序中@@标号和跳转指令之间的距离最好限制在编辑器能够显示的同一屏幕的范围内。
3.3.2 全局变量
1. 全局变量的定义
全局变量的作用域是整个程序,Win32汇编的全局变量定义在 .data或 .data?段内,可以同时定义变量的类型和长度,格式是:
变量名 类型 初始值1,初始值2,......
变量名 类型 重复数量 dup (初始值1,初始值2,......)
MASM中可以定义的变量类型相当多,具体如表3.2所示。
表3.2 变量的类型
名称 | 表示方式 | 缩写 | 长度(字节) |
字节 | byte | db | 1 |
字 | word | dw | 2 |
双字(doubleword) | dword | dd | 4 |
三字(farword) | fword | df | 6 |
四字(quadword) | qword | dq | 8 |
十字节BCD码(tenbyte) | tbyte | dt | 10 |
有符号字节(signbyte) | sbyte | 1 | |
有符号字(signword) | sword | 2 | |
有符号双字(signdword) | sdword | 4 | |
单精度浮点数 | real4 | 4 | |
双精度浮点数 | real8 | 8 | |
10字节浮点数 | real10 | 10 |
所有使用到变量类型的情况中,只有定义全局变量的时候类型才可以用缩写,现在先来看全局变量定义的几个例子:
.data wHour dw ? ;例1 wMinute dw 10 ;例2 _hWnd dd ? ;例3 word_Buffer dw 100 dup (1,2) ;例4 szBuffer byte 1024 dup (?) ;例5 szText db 'Hello,world!' ;例6
● 例1定义了一个未初始化的word类型变量,名称为wHour。
● 例2定义了一个名为wMinute的word类型变量。
● 例3定义了一个双字类型的变量_hWnd。
● 例4定义了一组字,以0001,0002,0001,0002,...的顺序在内存中重复100遍,一共是200个字。
● 例5定义了一个1 024字节的缓冲区。
● 例6定义了一个字符串,总共占用了12字节。两头的单引号是定界的符号,并不属于字符串中真正的内容。
在byte类型变量的定义中,可以用引号定义字符串和数值定义的方法混用,假设要定义两个字符串“Hello,World! ”和“Hello again”,每个字符串后面跟回车和换行符,最后以一个0字符结尾,可以定义如下:
szText db 'Hello,world!',0dh,0ah,'Hello again',0dh,0ah,0
2. 全局变量的初始化值
全局变量在定义中既可以指定初值,也可以只用问号预留空间,在 .data?段中,只能用问号预留空间,因为 .data?不能指定初始值,这里就有一个问题:既然可以用问号预留空间,那么在实际运行的时候,这个未初始化的值是随机的还是确定的呢?在全局变量中,这个值就是0,所以用问号指定的全局变量如果要以0为初始值的话,在程序中可以不必为它赋值。
上页:第03章 使用MASM · 3.2 调用API(3) 下页:第03章 使用MASM · 3.3 标号、变量和数据结构(2)