1.9 在MASM中声明内存变量
在汇编语言中,虽然可以使用数字地址引用内存,但这样做非常麻烦,而且容易出错。与其在程序中声明如下语句:“获取内存地址192处的32位值,获取内存地址188处的16位值”,不如采用这个声明更加优雅:“获取变量elementCount的内容,获取变量portNumber的内容”。使用变量名而不是内存地址,可以使程序更易于编写、阅读和维护。
为了创建(可写入的)数据变量,必须将数据变量放在MASM源文件的数据段中,数据段使用“.data”伪指令定义。该伪指令指示MASM:以下所有语句(直到下一个“.code”伪指令或其他段定义伪指令出现)将定义数据声明,并被分组到内存的读取/写入段中。
在“.data”段中,MASM允许用户使用一组数据声明伪指令来声明变量对象。数据声明伪指令的基本形式如下:
label directive?
其中,label是一个合法的MASM标识符,directive是表1-2中显示的一个伪指令。
表1-2 MASM数据声明伪指令
问号(?)操作数指示MASM:当程序加载到内存中时,对象是没有显式值的(默认初始化为0)。如果要使用显式值对变量进行初始化,则可以使用具体的初始值替换问号操作数。例如:
hasInitialValue sdword-1
表1-2中的一些数据声明伪指令(带有s前缀的伪指令)具有有符号版本。在大多数情况下,MASM会忽略此前缀。用户编写的机器指令会区分有符号和无符号操作,MASM本身则通常不关心变量是取有符号值还是无符号值。事实上,MASM允许以下两种情况:
.data
u8 byte-1;允许负的初始值
i8 sbyte 250;虽然+128是最大的有符号字节值,但此处仍然使用250
MASM关心的只是初始值是否放入一个字节中。即使“-1”不是一个无符号值,也可以放入内存的一个字节中。即使“250”超出了一个有符号8位整数的数值范围(请参阅2.7节的相关内容),MASM也会接受该初始值,因为它可以放入一个字节变量中(作为一个无符号数值)。
可以在单个数据声明伪指令中,为多个数据值保留存储空间。字符串多值数据类型对本章至关重要(后面的章节将讨论其他数据类型,如第4章中的数组)。用户可以使用如下byte伪指令在内存中创建以null结尾的字符串:
;0(null)结尾的C/C++字符串。
strVarName byte 'String of characters',0
请注意,字符串后面包含“,0”。在任何数据声明(不仅仅是字节声明)中,都可以在操作数字段中放置多个数据值,并用逗号分隔各数据值,MASM将为每个操作数定义一个指定大小和值的对象。对于字符串值(在本例中字符串值包括在单引号中),MASM为字符串中的每个字符定义一个字节(加上一个零字节,表示字符串末尾的“,0”操作数)。MASM允许用户使用单引号或双引号来定义字符串,并且必须使用与字符串开头相同的分隔符(单引号或双引号)终止字符串。
1.9.1 将内存地址与变量关联
使用MASM这样的汇编器/编译器的一个优势在于,用户不必关心数值形式的内存地址。用户只须在MASM中声明一个变量,MASM会将该变量与一组唯一的内存地址相关联。例如,假设用户定义了以下声明段:
.data
i8 sbyte ?
i16 sword ?
i32 sdword ?
i64 sqword ?
MASM将在内存中查找一个未使用的(8位)字节,并将其与变量i8相关联;查找两个未使用且连续的字节,并将这两个字节与变量i16相关联;查找四个未使用且连续的字节,并将这四个字节与变量i32相关联;查找八个未使用且连续的字节,并将这八个字节与变量i64相关联。用户只需要按名称引用这些变量,通常不必关心变量所关联的数值地址。不过,用户应该知道是MASM完成了这些关联操作。
当MASM处理“.data”段中的声明时,将为每个变量分配连续的内存位置[5]。假设前面声明的变量i8位于内存地址101,则MASM将把表1-3中出现的地址分配给变量i8、i16、i32和i64。
当数据声明语句中有多个操作数值时,MASM会按照值在操作数字段中出现的顺序将值输出到连续的内存位置。与数据声明关联的标签(如果存在)将与第一个(最左侧)操作数值的地址相关联。具体请参见第4章。
表1-3 变量地址分配
1.9.2 将数据类型与变量关联
在汇编过程中,MASM将数据类型与用户定义的每个标签(包括变量)相关联。这对于汇编语言来说属于相当高级的操作,因为大多数汇编器只是将一个值或地址与一个标识符相关联。
在大多数情况下,MASM使用变量的大小(字节)作为其数据类型(请参见表1-4)。
表1-4 MASM数据类型
后续章节将全面讨论proc、label、constant和text类型。