通过《宏简述》一节的学习,我们已经对宏有了一定的了解,下面来介绍一下宏的一些特性。
利用 REQ 限定符,可以指定必需的宏形参。如果被调用的宏没有实参与规定形参相匹配,那么汇编器将显示出错消息。如果一个宏有多个规定形参,则每个形参都要使用 REQ 限定符。
下面是宏 mPutChar,形参 char 是必需的:
mPutchar MACRO char:REQ
push eax
mov al,char
call WriteChar
pop eax
ENDM
宏定义中的注释行一般都出现在每次宏展开的时候。如果希望忽略宏展开时的注释,就在它们的前面添加双分号 (;;)。示例如下:
mPutchar MACRO char:REQ
push eax ;; 提示:char 必须包含 8 个比特
mov al, char
call WriteChar
pop eax
ENDM
在程序汇编时,ECHO 伪指令写一个字符串到标准输出。下面的 mPutChar 在汇编时会显示消息“Expanding the mPutChar macro” :
mPutchar MACRO char:REQ
ECHO Expanding the mPutchar macro
push eax
mov al,char
call WriteChar
pop eax
ENDM
Visual Studio 2012 的控制台窗口不会捕捉 ECHO 伪指令的输出,除非在编写程序时将其设置为生成详细输出。设置方法如下:从 Tool 菜单选择 Options,选择 Projects and Solutions,选择 Build and Run,再从 MSBuild project build output verbosity 下拉列表中选择 Detailed。或者打开一个命令提示符并汇编程序。
首先,执行如下命令,调整 Visual Studio 当前版本的路径:
然后,键入如下指令,其中 filename.asm 是程序的源代码文件名:
宏定义中常常包含了标号,并会在其代码中对这些标号进行自引用。例如,下面的宏 makeString 声明了一个变量 string,且将其初始化为字符数组:
makestring MACRO text
.data
string BYTE text,0
ENDM
假设两次调用宏:
由于汇编器不允许两个标号有相同的名字,因此结果出现错误:
为了避免标号重命名带来的问题,可以对一个宏定义内的标号使用 LOCAL 伪指令。若标号被标记为 LOCAL,那么每次进行宏展开时,预处理程序就把标号名转换为唯一的标识符。下面是使用了 LOCAL 的宏 makeString:
makeString MACRO text
LOCAL string
.data
string BYTE text,0
ENDM
假设和前面一样,也是两次调用宏,预处理程序生成的代码会将每个string替换成唯一 的标识符:
汇编器生成的标号名使用了 ??nnnn 的形式,其中 nnnn 是具有唯一性的整数。local 伪指令还可以用于宏内的代码标号。
宏通常既包含代码又包含数据。例如,下面的宏mWrite在控制台显示文本字符串:
mWrite MACRO text
LOCAL string ;;local号
.data ;;定义字符串
string BYTE text,0
.code
push edx
mov edx, OFFSET string
call WriteString
pop edx
ENDM
下面的语句两次调用宏,并向其传递不同的字符串文本:
汇编器对这两条语句进行展开时,每个字符串都被赋予了唯一的标号,且MOV指令也 作了相应的调整:
被其他宏调用的宏称为被嵌套的宏 (nested macro)。当汇编器的预处理程序遇到对被嵌套宏的调用时,它会就地展开该宏。传递给主调宏的形参也将直接传递给它的被嵌套宏。
提示:使用模块方法创建宏。保持它们的简短性,以便将它们组合到更复杂的宏内。这样有助于减少程序中的复制代码量。
【示例】下面的宏 mWritein 写一个字符串文本到控制台,并添加换行符。它调用宏 mWrite 和 Crlf 过程:
mWriteln MACRO text
mWrite text
call Crlf
ENDM
形参 text 被直接传递给 mWrite。假设用下述语句调用 mWriteln:
在结果代码展开,语句旁边的嵌套级数(2)表示被调用的是一个嵌套宏: