2014-01-19 40 views
2

查看这个用VC++ 2003/2005编译的C代码。为什么要将4字节参数传递给需要2字节参数的函数?

#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()功能需要两个WORD(2字节)的论点,和完美的作品(输出5 + 6 = 11)。

1)为什么这项工作?在调试它抛出一个分段错误

__asm { 
    MOV WORD PTR [EAX], 5 
    MOV WORD PTR [EBX], 6 
    PUSH EBX 
    PUSH EAX 
    CALL SomeFunction 
} 

知道该函数有两个参数WORD,我会做到这一点。

2)为什么这个不是工作?

在此先感谢!

+2

“完美地工作”可能对不同的人有不同的含义。 –

+0

现在就来看看吧。 – cdonts

回答

3

第一个工作,因为Win32 ABI说任何大小小于或等于4个字节的参数都是以4个字节的形式传递的,必要时填充。所以16位字实际上是以32位传递的。这就是你正在做的。

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

MOV WORD PTR [EAX], 5 

该行通过EAX移动5到开始的内存位置的16位字指向。但是EAX以前没有装载有效的内存地址。此外,你在推动指针(一个WORD*?)。

为了通过在堆栈中的16位值,你可以使用:

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

但是去agains在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位结构。

+0

所以我的第一次ASM调用是更好的方法,即使参数为2字节? – cdonts

+0

@cdonts:的确如此!该代码有效,不是偶然的,而是因为它是正确的。如果你编译'SomeFunction(5,6)',你会看到编译器生成一个类似的代码。 – rodrigo

+0

完美,谢谢你的帮助! – cdonts

相关问题