为什么将4字节参数传递给期望2字节参数的函数?

时间:2014-01-19 23:46:15

标签: c visual-c++ assembly x86 arguments

查看使用VC ++ 2003/2005编译的此C代码。

#include <stdio.h>
#include <windows.h>

void WINAPI SomeFunction(WORD a, WORD b)
{
    printf("%d + %d = %d\n", a, b, a + b);
}

int main()
{
    __asm {
        MOV EAX, 5
        MOV EBX, 6
        PUSH EBX
        PUSH EAX
        CALL SomeFunction
    }

    return 0;
}

在这个ASM代码中,我将两个DWORD(4字节)参数(据我所知默认情况下)传递给期望两个SomeFunction()的{​​{1}}函数(2 bytes)参数,并且完美地工作(输出WORD)。

1)为什么会这样?

知道该函数需要两个5 + 6 = 11个参数,我会这样做:

WORD

调试时会抛出分段错误。

2)为什么有效?

提前致谢!

1 个答案:

答案 0 :(得分:3)

第一个可行,因为Win32 ABI表示任何大小小于或等于4个字节的参数都将作为4个字节传递,必要时填充。因此,16位字实际上作为32位传递。这就是你在做什么。

第二个不起作用,因为它做了不同的事情:

MOV WORD PTR [EAX], 5

该行将5移动到从EAX指向的内存位置开始的16位字。但是EAX以前没有加载有效的内存地址。此外,您正在推动指针(WORD*?)。

为了在堆栈中传递16位值,您可以使用:

MOV AX, 5
MOV BX, 6
PUSH AX
PUSH BX

但这又是Win32 ABI,因为堆栈总是32位对齐。

有趣的是,如果您通过值(未经测试)传递此结构将会有效:

struct WW
{
    WORD a, b;
};

void WINAPI SomeFunction(WW w)
{
    printf("%d + %d = %d\n", w.a, w.b, w.a + w.b);
}

int main()
{
    __asm {
        MOV BX, 6 // the parameters are reversed, methinks
        MOV AX, 5
        PUSH BX
        PUSH AX
        CALL SomeFunction
    }

    return 0;
}

那是因为结构的字段以4个字节(sizeof(WW)==4)打包,所以这是复制到堆栈的内容。

当然,使用16位寄存器并不好玩。这可能更好:

MOV EAX 0x00060005
PUSH EAX

一次性复制整个32位结构。