繼續談下呼叫約定

2021-05-28 00:54:44 字數 2062 閱讀 4242

今天寫了個dll是用extern "c"匯出的,以前一直用.def檔案,結果出現了以下錯誤

無法解析的外部符號 "__declspec(dllimport) int __cdecl setstatus(int)" (__imp_?setstatus@@yahh@z),該符號在函式 "public: void __thiscall ctestantiremotedlg::onbnclickedbtn(void)" (?onbnclickedbtn@ctestantiremotedlg@@qaexxz) 中被引用

解決是需要在匯出的前面也加extern "c"。

如果定義的約定和使用的約定不一致,則將導致堆疊被破壞,導致嚴重問題,下面是兩種常見的問題:

1) 函式原型宣告和函式體定義不一致2) dll匯入函式時宣告了不同的函式約定

1._cdecl是c declaration的縮寫,表示c語言預設的函式呼叫方法:所有引數 從右到左依次入棧,這些引數由呼叫者清除,稱為手動清棧(由呼叫者把引數彈出棧)。對於傳送引數的記憶體棧是由呼叫者來維護的(正因為如此,實現可變引數的函式只能使用該呼叫約定)。被呼叫函式無需要求呼叫者傳遞多少引數,呼叫者傳遞過多或者過少的引數,甚至完全不同的引數都不會產生編譯階段的錯誤。

_cdecl是c和c++程式的預設呼叫方法。每乙個呼叫它的函式都包含清空堆疊的**,所以產生的可執行檔案大小會比呼叫_stdcall函式的大。vc將函式編譯後會在函式名前面加上下劃線字首。是mfc預設呼叫約定。

2._stdcall是standard call的縮寫,是c++的標準呼叫方式:所有引數從右到左依次入棧,如果是呼叫類成員的話,最後乙個入棧的是this指標。這些堆疊中的引數由被呼叫的函式在返回後清除,使用的指令是retn x,x表示引數占用的位元組數,cpu在ret之後自動彈出x個位元組的堆疊空間。稱為自動清棧。函式在編譯的時候就必須確定引數個數,並且呼叫者必須嚴格的控制引數的生成,不能多,不能少,否則返回後會出錯。

_stdcall是pascal程式的預設呼叫方式,通常用於win32api中。vc將函式編譯後會在函式名前面加上下劃線字首,在函式名後加上「@」和引數的位元組數。

3._fastcall呼叫約定是「人」如其名,它的主要特點就是快,因為它是通過暫存器來傳送引數的(實際上,它用ecx和edx傳送前兩個雙字(dword)或更小的引數(或若干個),剩下的引數仍舊自右向左壓棧傳送,被呼叫的函式在返回前清理傳送引數的記憶體棧),在函式名修飾約定方面,它和前兩者均不同。vc將函式編譯後會在函式名前面加上「@」字首,在函式名後面加上「@」和引數的位元組數。

5.naked call採用1-4的呼叫約定時,如果必要的話,進入函式時編譯器會產生**來儲存esi,edi,ebx,ebp暫存器,退出函式時則產生**恢復這些 暫存器的內容。naked call不產生這樣的**。naked call不是型別修飾符,故必須和_declspec共同使用。

以上是貼上,以前在看鬱金香的教程時看到在函式前面加個naked 說生成純彙編,因為如果用內聯彙編的方式,編譯器仍然會在函式內部的開始和結束部分自動做一些工作。如果想要編寫乙個純彙編的函式,所有的棧初始化恢復工作都手動來寫,就可以加這個。

這是乙個很少見的呼叫約定,一般程式設計者建議不要使用。編譯器不會給這種函式增加初始化和清理**,更特殊的是,不能用return返回返回值,只能用插入彙編返回結果。這一般用於實模式驅動程式設計,假設定義乙個求和的加法程式,可以定義為:

__declspec(naked) int  add(int a,int b)

注意,這個函式沒有顯式的return返回值,返回通過修改eax暫存器實現,而且連退出函式的ret指令都必須顯式插入。上面**被翻譯成彙編以後變成:

mov    eax,[ebp+8]

add eax,[ebp+12]

ret 8

注意這個修飾是和__stdcall及cdecl結合使用的,前面是它和cdecl結合使用的**,對於和stdcall結合的**,則變成:

__declspec(naked) int __stdcall function(int a,int b)

至於這種函式被呼叫,則和普通的cdecl及stdcall呼叫函式一致。

論調用約定

在c語言中,假設我們有這樣的乙個函式 int function int a,int b 呼叫時只要用result function 1,2 這樣的方式就可以使用這個函式。但是,當高階語言被編譯成計算機可以識別的機器碼時,有乙個問題就凸現出來 在cpu中,計算機沒有辦法知道乙個函式呼叫需要多少個 什麼...

過程呼叫約定

過程呼叫約定 當程式中的各個過程 函式 被分別編譯時,關鍵是如何規定暫存器的使用規則。當編譯某個過程時,編譯器必須事先知道需要用到哪些暫存器 哪些暫存器的內容需要保留給其他過程使用等資訊。我們稱這些暫存器的使用規則為暫存器使用約定或過程呼叫約定。顧名思義,大多數情況下,這些規定主要是用於約束軟體的,...

函式呼叫約定

函式呼叫約定有多種,這裡簡單說一下 1 stdcall 呼叫約定相當於16位動態庫中經常使用的 pascal 呼叫約定。在32位的vc 5.0中pascal呼叫約定不再被支援 實際上它已被定義為 stdcall。除了 pascal外,fortran和 syscall也不被支援 取而代之的是 stdc...