汇编语言过程参数简介

< 上一页PROTO伪指令 WriteStackFrame过程下一页 >
过程参数一般按照数据在主调程序和被调用过程之间传递的方向来分类:

1) 输入类

输入参数是指从主调程序传递给过程的数据。被调用过程不会被要求修改相应的参数变量,即使修改了,其范围也只能局限在自身过程中。

2) 输出类

当主调程序向过程传递变量地址,就会产生输岀参数。过程用地址来定位变量,并为其分配数据。比如,Win32 控制台库中的 ReadConsole 函数,其功能为从键盘读入一个字符串。用户键入的字符由 ReadConsole 保存到缓冲区中,而主调程序传递的就是这个字符串缓冲区的指针:

.data
buffer BYTE 80 DUP(?)
inputHandle DWORD ?
.code
INVOKE ReadConsole, inputHandle, ADDR buffer,
    (etc.)

3) 输入输出类

输入输出参数与输出参数相同,只有一个例外:被调用过程预期参数引用的变量中会包含一些数据,并且能通过指针来修改这些变量。

【示例】下面的例子实现两个 32 位整数的交换。Swap 过程有两个输入输出参数 pValX 和 pValY,它们是交换数据的地址:
; Swap 过程示例   (Swap.asm)
INCLUDE Irvine32.inc

Swap PROTO, pValX:PTR DWORD, pValY:PTR DWORD

.data
Array DWORD 10000h,20000h

.code
main PROC
    ; 显示交换前的数组
    mov  esi,OFFSET Array
    mov  ecx,2                     ; 计数值 = 2
    mov  ebx,TYPE Array
    call DumpMem                   ; 显示数组

    INVOKE Swap,ADDR Array, ADDR [Array+4]
    ; 显示交换后的数组
    call DumpMem
    exit
main ENDP

;-------------------------------------------------------
Swap PROC USES eax esi edi,
    pValX:PTR DWORD,              ; 第一个整数的指针
    pValY:PTR DWORD               ; 第二个整数的指针
; 交换两个32位整数的值
; 返回: 无
;-------------------------------------------------------
    mov esi,pValX                 ; 获得指针
    mov edi,pValY
    mov eax,[esi]                 ; 取第一个整数
    xchg eax,[edi]                ; 与第二个数交换
    mov [esi],eax                 ; PROC 在这里生成 RET 8
    ret
Swap ENDP
END main
Swap 程的两个参数 pValX 和 pValY 都是输入输出参数。它们的当前值要输入到过程,而它们的新值也会从过程输出。由于使用的 PROC 带有参数,汇编器把 Swap 过程末尾的 RET 指令改为 RET 8(假设调用规范是 STDCALL)。

调试提示

这里提醒编程者要注意的一些常见错误是汇编语言在传递过程参数时会遇到的,希望编程者永远不要犯这些错误。

1) 参数大小不匹配

数组地址以其元素的大小为基础。比如,一个双字数组第二个元素的地址就是其起始地址加 4。假设调用 Swap 过程,并传递 DoubleArray 前两个元素的指针。如果错误地把第二个元素的地址计算为 DoubleArray+1,那么调用 Swap 后,DoubleArray 中的十六进制结果值也不正确:

.data
DoubleArray DWORD 10000h,20000h
.code
INVOKE Swap, ADDR [DoubleArray+0], ADDR [DoubleArray+1]

2) 传递错误类型的指针

在使用 INVOKE 时,要记住汇编器不会验证传递给过程的指针类型。例如,Swap 过程期望接收到两个双字指针,假若不小心传递的是指向字节的指针:

.data
ByteArray BYTE 10h,20h,30h,40h,50h,60h,70h,80h
.code
INVOKE Swap, ADDR [ByteArray+0], ADDR [ByteArray+1]

程序可以汇编运行,但是当 ESI 和 EDI 解引用时,就会交换两个 32 位数值。

3) 传递立即数

如果过程有一个引用参数,就不要向其传递立即数参数。考虑下面的过程,它只有一个引用参数:
Sub2 PROC, dataPtr:PTR WORD
    mov esi, dataPtr         ;获得地址
    mov WORD PTR [esi], 0    ;解引用,分配零
    ret
Sub2 ENDP
汇编下面的 INVOKE 语句将导致一个运行时错误。Sub2 过程接收 1000h 作为指针的值,并解引用到内存地址 1000h:

INVOKE Sub2, 1000h

上例很可能会导致一般性保护故障,因为内存地址1000h不大可能在该程序的数据段中。
< 上一页PROTO伪指令 WriteStackFrame过程下一页 >