You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MASM中通过引用传递参数时获取错误值的问题排查

MASM中值传递与引用传递的差异问题

我正在学习MASM中按引用传递并访问值的方法,先写了一个使用Irvine.inc库的简单程序,用来显示val1和val2的值:

INCLUDE Irvine32.inc
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCall: DWORD

.data               ;date values
    array DWORD 100, 100, 200
    val1 DWORD 50
    val2 DWORD 6000
    charVal BYTE 'x', 0

.code               ;Code
main PROC
    
    mov eax, val1               ;this is a test to see if it works properly. 
    call WriteDec               ;writes eax in Decimal form to the console
    
    push OFFSET val1            ;push two values by reference onto the stack
    push OFFSET val2            
    call PrintNum

    INVOKE ExitProcess, 0
main ENDP

PrintNum PROC
    push ebp                    ;save ebp so we can go back to the program
    mov ebp, esp                ;esp should point to the top of the stack
    pushad                      ;why do we need to do this?
    mov eax, 0                  ;zero out eax because I though that might be the problem
    mov eax, [ebp+12]           ;this should move what is in the value of ebp+12 into eax (val1)
    call WriteDec               ;this writes what is in eax to the console
    mov eax, [ebp+8]            ;this should move what is in the value of ebp+8 into eax (val2)
    call WriteDec               ;this writes what is in eax to the console
    pop ebp                     ;get back to correct ebp section of the code
    popad                       ;needed to do this because pushad from before
    ret
PrintNum ENDP
End main

这个程序运行后无法正确输出地址中的值,后来按照@Jester的建议添加mov eax, [eax]后,程序能够正常运行。但随后我编写了另一个测试程序,未添加该指令却也能正常运行:

.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCall: DWORD

.data               ;date values
    val1 DWORD 200
    val2 DWORD 100

.code               ;Code
main PROC
    push val1       ;needed to add the offset not the value
    push val2       ;needed to add the offset not the value
    call PrintVars


    INVOKE ExitProcess, 0
main ENDP

PrintVars PROC
    push ebp
    mov ebp, esp
    mov eax, [ebp+12]   ;mov address of val1 into eax
    ;mov eax, [eax]     ;use address to get actual value
    call WriteDec       ;write the dec to the console
    call Crlf           ;carage return
    mov eax, [ebp+8]    ;mov address of val2 into eax
    ;mov eax, [eax]     ;deference address to actual value
    call WriteDec       ;write the dec to console
    call Crlf           ;carage return
    pop ebp
    ret
PrintVars ENDP
End main

想请教这两个程序之间的差异是什么?


核心差异:传递的是「变量值」还是「内存地址」

  • 第一个程序:按引用传递(传递地址)
    你在main中使用push OFFSET val1/push OFFSET val2,压入栈的是val1和val2的内存地址(而非变量本身的数值)。因此在PrintNum中,[ebp+12]和[ebp+8]读取到的是地址值,直接用WriteDec输出的是地址的十进制数值,不是变量的值。必须通过mov eax, [eax]完成解引用,从地址指向的内存中取出实际变量值,才能正确输出。

  • 第二个程序:按值传递(传递变量值)
    你在main中使用push val1/push val2,压入栈的是val1和val2的实际数值(而非地址)。因此在PrintVars中,[ebp+12]和[ebp+8]直接读取到的就是变量的数值,无需额外解引用,WriteDec可以直接输出正确结果。

另外补充第一个程序的小问题:
PrintNum中pop ebp和popad的顺序错误——栈是后进先出结构,你先执行了pushad再push ebp,所以出栈时应该先执行popad恢复寄存器,再执行pop ebp恢复基址指针,否则会导致寄存器状态混乱。

内容的提问来源于stack exchange,提问作者Funlamb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 21:13:23