2013-06-22 88 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:我在回答中添加了更多信息。 –