2008-10-04 104 views
6

演示如何使用MFC創建線程的示例代碼將線程函數聲明爲static和__cdecl。爲什麼需要後者?提升線程不會打擾這個約定,那麼它只是一個時代錯誤?爲什麼線程函數需要聲明爲'__cdecl'?

例如(MFC):

static __cdecl UINT MyFunc(LPVOID pParam) 
{ 
... 
} 

CWinThread* pThread = AfxBeginThread(MyFunc, ...); 

而提升:

static void func() 
{ 
... 
} 

boost::thread t; 
t.create(&func); 

(因爲我無處一個IDE附近的代碼樣本可能不是100%正確的)。

__cdecl有什麼意義?創建線程時它有什麼幫助?

回答

4

__cdecl告訴編譯器使用C調用約定(與stdcall,fastcall或其他編譯器支持的調用約定相反)。我相信,VC++默認使用stdcall。

調用約定會影響諸如參數如何被壓入棧(或者寄存器,如果是fastcall)以及誰從堆棧(調用者或被調用者)彈出參數。

在Boost的情況下。我相信它使用模板特化來找出適當的函數類型和調用約定。

+0

Boost不考慮調用約定。這不是語言級別的功能(更多是鏈接器級別功能)。 MS使用它來代碼的向後兼容性。 – 2008-10-04 18:43:34

+0

Loki有最佳答案 – SChalice 2015-11-24 00:08:45

1

因爲你的線程將被一個運行時函數調用,該函數爲你管理這個函數,並且該函數期望它是這樣的。 Boost設計了一種不同的方式。

在線程函數的開始處放置一個斷點,並在調用堆棧時查看堆棧,您將看到調用您的運行時函數。

4

看那原型AfxBeginThread()

CWinThread* AfxBeginThread(
    AFX_THREADPROC pfnThreadProc, 
    LPVOID pParam, 
    int nPriority = THREAD_PRIORITY_NORMAL, 
    UINT nStackSize = 0, 
    DWORD dwCreateFlags = 0, 
    LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
); 

AFX_THREADPROCUINT(AFX_CDECL*)(LPVOID)一個typedef。當你將一個函數傳遞給AfxBeginThread()時,它必須匹配該原型,包括調用約定。

關於__cdecl__stdcall(以及__fastcall__thiscall)的MSDN頁面解釋了每個調用約定的優缺點。

構造函數boost::thread使用模板來傳遞函數指針或可調用函數對象,因此它沒有與MFC相同的限制。

1

C/C++編譯器默認使用C調用約定(在堆棧中首先推入最右邊的參數),因爲它允許使用帶有可變參數編號的函數作爲printf。

帕斯卡調用約定(又名「fastcall」)首先推動最左邊的參數。儘管你需要使用一些技巧,但是這會讓你更容易使用可變參數函數(我讀過它們仍然有可能的地方)。

由於使用Pascal慣例產生的速度,默認情況下Win32和MacOS API都使用該調用約定,除非在某些情況下。

如果該函數只有一個參數,理論上使用任何一個調用約定都是合法的,儘管編譯器可能強制使用相同的調用約定來避免任何問題。

boost庫的設計注重可移植性,因此它們應該是不可知的,特定編譯器使用哪個調用者約定。

1

真正的答案與Windows如何在內部調用線程proc例程有關,它期望函數遵守特定的調用約定,在本例中是一個宏,WINAPI,根據我的系統是定義爲:

#define WINAPI  __stdcall 

這意味着被調用函數負責清理堆棧。 boost :: thread能夠支持任意函數的原因是,它將一個指向將線程:: create函數調用到CreateThread的函數對象的指針。與線程關聯的threadproc只需在函數對象上調用operator()。

因此,MFC需要__cdecl的原因與內部調用傳遞給AfxBeginThread調用的函數的方式有關。沒有充足的理由這樣做,除非他們計劃允許可變參數...

相關問題