轉帖|其它|編輯:郝浩|2009-02-20 11:05:10.000|閱讀 731 次
概述:本文是一篇關于調用約定的小教程,旨在解釋調用約定如何運作,并且與C和C + +中書寫動態鏈接庫以及在C#利用他們相關聯。這可能對理解骨架代碼有用。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
當您開始使用動態鏈接庫,編寫涉及其他語言中的函數代碼時,你會遇到諸如stdcall,safecall,cdecl和winapi之類的詞。這些調用約定定義了如何在運行時調用外部函數,即使是單獨編譯,甚至可能是和不同的編譯器和連接器。
本文是一篇關于調用約定的小教程,旨在解釋調用約定如何運作,并且與C和C + +中書寫動態鏈接庫以及在C#利用他們相關聯。這可能對理解骨架代碼有用。
備注:這無關鏈接,而是使用動態數據庫并在運行時間調用它們。
一個調用約定定義了參數如何被傳到堆棧,調用者或函數是否需要在結束調用后清理堆棧。
關于堆棧
堆棧追蹤函數的調用來源。而這通常是硬件通過使用注冊簿來完成的,注冊簿是對記憶的指向標。按照慣例,一個堆棧開始是指向內存。隨著每個項目都堆到堆棧上,堆棧指針就會減少。當你從堆棧上刪除數據時。
一般來說,編譯器具備一些設置可以指定堆棧可得的內存數量。堆棧可以保存以下三種類型的數據:
CPU會有一些針對堆棧記憶分配的特殊指令。編譯器會計算出所有本地變量和參數所需要的內存量,并且相應地對其進行內存分配。在調用快結束時,準確的反面指令被調用來減少同樣的量。基本上,這些指令通過登記的數額減少了堆棧指針,并且在最后又相應地增加了空間。
返回地址
當一個函數被調用的時候,CPU要做的第一件事是在調用的函數生成時,計算出下一個指令是什么。下表就是對該過程進行的一個小小的演示:
Address 101
Address 102 Call Function 201
Address 103...
...
Address 151 Call Function 201
Address 152...
Address 201.. First instruction of function
Address 202.. Allocate memory for local variables
...
Address 206.. Release the allocated memory
Address 207.. return from function
在上面的例子中,如果CPU剛剛在101處理完指令,那么其下一步操作就是處理102的指令并且調用201的函數。那之后的指令就是103。它會一直持續到151直到再次調用201的函數。這次,當它返回時,它會一直與152的指令持續。地址103和152是保存在堆棧的返回地址。
堆棧狀態
如果堆棧以5000的指標開始,它就會像這樣:
Stack Pointer= 5000
Address 5000
當函數201在202指令運行完后被調用,堆棧就會有數據。讓我們想象一下這個函數沒有參數但是有一個擁有本地變量總數,該總數是由10個整數組成的數組。
int totals[10]
如果每個地址保存一個整數,那么這個數組就有十個地點。在201,堆棧指數會減少10。
Stack Pointer= 4989
Address 5000 .. 103
Address 4999 .. total[9]
Address 4998 .. total[8]
...
Address 4990 .. total[0]
Address 4989 <- Current top of stack.
執行206時,堆棧指針將增加10個。207的指令會彈到堆棧的極限。這一情況正是一種警告。與推送一起,某個值也被存儲在當前地址中,該地址由堆棧指針持有,然后堆棧指針遞減。膨脹使堆棧指針增加了1,然后從堆棧指針所持有的地址處得到值。因此,從5000(地址)得到值,即103 ,而這就是下一個要執行的指令。
由數值審核還是由參考審核?
前者把整個的變量復制到堆棧上,這不但很慢而且如果變量很大,可能使堆棧無法承擔。例如,10,000個數組會減少10,000個堆棧指數并且把這10,000個整數的值到復制到堆棧上。相比之下,使用參考審核則要快得多,因為它只會把變量的地址送到堆棧上。所以在C語言中是用指數(*),在c++中使用參考(&)來保持其簡便。[SPAN]
調用約定
了解參數是很重要的,比方,a,b,c,它們是按照這個順序送到堆棧的嗎?還是按照c,b,a的順序?返回地址會去到哪里呢?同樣,被調用的函數從堆棧刪除參數還是從主程序?為什么不從登記簿通過數值來加速呢?指定發生的事情就是調用約定所做的事。
約定列表
主要的約定如下所示。
function f(A, B, C)
除非你的dlls只被C或C++使用,否則的話,你就相信winapi。則會使默認情況,所以不需要指定。
C++中的名字改編
因為C + +語言是一種安全且相當嚴謹的語言類型,它提出了名字改編。這意味著,輸出的函數,包括來自類型的參數類型的額外函數。這有助于防止函數被調用時出現參數錯誤,而這種錯誤可能會導致莫名的死機。然而,當非C + +代碼調用C + +的函數時也可能會出現問題。例如,你有一個C #應用程序調用dll的C + =代碼。由于dll中的函數名稱毀壞,函數就無法被找到。操作系統可能會期望一個叫做GetValue(int a, float b)的函數,但在該dll中,名稱錯位后看起來就像GetValueif。
有一種簡單的修復方法。任何C++ dll必須把代碼在任何輸出函數之前和之后的說明中。就像這樣:
#define MYEXPORT __declspec(dllexport)
#ifdef __cplusplus
extern "C" {
#endif
MYEXPORT char * WINAPI GetBotName(void) ; // returns name of your Bot
...
#ifdef __cplusplus
}
#endif
這一行:#define MYEXPORT __declspec(dllexport),就告訴了編譯器在任何由MYEXPORT定于的函數上添加_declspec。這使得該函數可見與使用dll的任何應用程序。
macro_cplusplus只定義在兼容C++編譯器的Ansi 98中。所以macro會被所有包含在C文件夾的頁眉文件忽視。
在你擁有C代碼的反例中,你希望與C++鏈接,那樣你就把說明用這個語句包裝起來:
extern “C++” {
這一操作可迫使編譯器粉碎函數名稱。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:IT專家網