2013-06-22 51 views
3

我收到了一些意外的行为,这可能意味着我不完全理解编译器在做什么。请看下面的做作程序:从程序集调用C++函数,使用struct参数

#include <stdio.h> 

#pragma pack(push, 1) 
struct A { 
    unsigned short a; 
    unsigned short b; 

    explicit A() { 
     printf("construct\n"); 
    } 
    ~A() { 
     printf("destruct\n"); 
    } 
}; 
#pragma pack(pop) 
static_assert(sizeof(A) == 4, "sizeof(A) != 4"); 

A __stdcall f(int p1, A p2, int p3, int p4) { 
    printf("%08X %08X %08X %08X\n", p1, p2, p3, p4); 
    return p2; 
} 

int main() { 
    __asm { 
     push 4 
     push 3 
     push 2 
     push 1 
     call f 

    } 
    return 0; 
} 

上述程序会崩溃,但如果我从struct A去除A()~A()的定义也不会。这个问题与编译器认为参数在堆栈中的位置有关,构造函数定义它认为它们比它们的位置多4个字节。如果我删除构造函数的输出我得到的是这样的:

00000001 00000002 00000003 00000004

这是我所期待的,但与构造函数定义我得到

00000002 00000003 00000004 00000000

这显然不是我所期待的。运行前者时,函数返回RETN 0x10,后者返回RETN 0x14,因此它看起来应该有另一个参数(为什么?)。我注意到,如果我将f更改为void函数,它将按预期工作。那么,有人可以向我解释发生了什么,为什么?我已关闭所有优化。

+0

'p2'属于'A'类型。然而,你正在传递一个int。我很惊讶,它可以工作,因为这是相当不明确的。你确定这是正确的代码吗? – Adrian

+0

@Adrian'A'与int的大小相同,并且没有虚函数,所以我不明白为什么它不起作用 – user1520427

+0

当A是非POD时,将会出现意想不到的行为。如果你真的想这样做,请反汇编编译器做什么来查看真正发生的事情。 – Adrian

回答

6

在assuembly级别,只有简单的值可以通过返回一个函数返回寄存器中,所以如果需要返回一个更复杂的对象,编译器会把它看作是你传递一个指向返回对象:

void f(A *return_ptr,int p1,A p2,int p3,int p4); 

但是可以进行某些优化。在你的例子中,你的类包含两个16位短路,而这两个16位短路可以打包成一个32位整数并返回寄存器。但是,如果您定义了一个自定义析构函数,那么该类不再被认为足够简单,无法执行此优化。

+0

非常感谢,我认为这可能是这样的。你知道为什么这个问题只会在我定义自己的构造函数时出现吗? – user1520427

+0

@ user1520427:我在回答中添加了更多信息。 –