__cdecl __stdcall区别-转
http://blog.sina.com.cn/s/blog_5fa3bba00100jkaa.html
最大的區別就是__cdecl是由調用函數者釋放堆棧,而__stdcall由被調用者釋放。但是我們在調用任一種函數時,都沒有進行顯示的釋放堆棧工作。這個工作由編譯器完成,它幫我們生成了這個中間要使用的代碼。
?
這兩種調用參數入棧方式一樣, 都是先函數參數入棧,再是函數返回地址入棧,再是函數局部變量入棧。 這兩種區別只是退棧時,誰來清除棧,這點不一樣。 如下例子,是被調函數本身自己清除(比如stdcall那個匯編語句 ret 8),還是調用方清除(比如cdecl的add esp, 8)這點不一樣。
?
以下系轉載:?????
?????在C語言中,假設我們有這樣的一個函數:
int function(int a,int b)
調用時只要用result = function(1,2)這樣的方式就可以使用這個函數。但是,當高級語言被編譯成計算機可以識別的機器碼時,有一個問題就凸現出來:在CPU中,計算機沒有辦法知道一個函數調用需要多少個、什么樣的參數,也沒有硬件可以保存這些參數。也就是說,計算機不知道怎么給這個函數傳遞參數,傳遞參數的工作必須由函數調用者和函數本身來協調。為此,計算機提供了一種被稱為棧的數據結構來支持參數傳遞。
當參數個數多于一個時,按照什么順序把參數壓入堆棧
函數調用后,由誰來把堆棧恢復原狀
在高級語言中,通過函數調用約定來說明這兩個問題。常見的調用約定有: stdcall
cdecl
fastcall
thiscall
naked call
1. stdcall調用約定
stdcall很多時候被稱為pascal調用約定,因為pascal是早期很常見的一種教學用計算機程序設計語言,其語法嚴謹,使用的函數調用約定就是stdcall。在Microsoft C++系列的C/C++編譯器中,常常用PASCAL宏來聲明這個調用約定,類似的宏還有WINAPI和CALLBACK。 stdcall調用約定聲明的語法為(以前文的那個函數為例):
int __stdcall function(int a,int b)
stdcall的調用約定意味著:1)參數從右向左壓入堆棧,2)函數自身修改堆棧 3)函數名自動加前導的下劃線,后面緊跟一個@符號,其后緊跟著參數的尺寸 以上述這個函數為例,參數b首先被壓棧,然后是參數a,函數調用function(1,2)調用處翻譯成匯編語言將變成: push 2 第二個參數入棧
push 1 第一個參數入棧
call function 調用參數,注意此時自動把cs:eip入棧 而對于函數自身,則可以翻譯為:
push ebp 保存ebp寄存器,該寄存器將用來保存堆棧的棧頂指針,可以在函數退出時恢復
mov ebp, esp 保存堆棧指針
mov eax,[ebp + 8H] 堆棧中ebp指向位置之前依次保存有ebp, cs:eip, a, b, ebp +8指向a
add eax,[ebp + 0CH] 堆棧中ebp + 12處保存了b
mov esp, ebp 恢復esp
pop ebp
ret 8 而在編譯時,這個函數的名字被翻譯成_function@8 注意不同編譯器會插入自己的匯編代碼以提供編譯的通用性,但是大體代碼如此。其中在函數開始處保留esp到ebp中,在函數結束恢復是編譯器常用的方法。 從函數調用看,2和1依次被push進堆棧,而在函數中又通過相對于ebp(即剛進函數時的堆棧指針)的偏移量存取參數。函數結束后,ret 8表示清理8個字節的堆棧,函數自己恢復了堆棧。
2.cdecl調用約定
cdecl調用約定又稱為C調用約定,是C語言缺省的調用約定,它的定義語法是: int function (int a ,int b) //不加修飾就是C調用約定
int __cdecl function(int a,int b)//明確指出C調用約定 在寫本文時,出乎我的意料,發現cdecl調用約定的參數壓棧順序是和stdcall是一樣的,參數首先由右向左壓入堆棧。所不同的是,函數本身不清理堆棧,調用者負責清理堆棧。由于這種變化,C調用約定允許函數的參數的個數是不固定的,這也是C語言的一大特色。對于前面的function函數,使用cdecl后的匯編碼變成: 調用處
push 1 ? ? ? ? ? ? ? ? ?(這里寫反了吧,應該是push 2)
push 2 ? ? ? ? ? ? ? ? ?(這里寫反了吧,應該是push 1)
call function
add esp, 8 注意:這里調用者在恢復堆棧 被調用函數_function處
push ebp 保存ebp寄存器,該寄存器將用來保存堆棧的棧頂指針,可以在函數退出時恢復
mov ebp,esp 保存堆棧指針
mov eax,[ebp + 8H] 堆棧中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a
add eax,[ebp + 0CH] 堆棧中ebp + 12處保存了b
mov esp,ebp 恢復esp
pop ebp
ret 注意,這里沒有修改堆棧 MSDN中說,該修飾自動在函數名前加前導的下劃線,因此函數名在符號表中被記錄為_function,但是我在編譯時似乎沒有看到這種變化。 由于參數按照從右向左順序壓棧,因此最開始的參數在最接近棧頂的位置,因此當采用不定個數參數時,第一個參數在棧中的位置肯定能知道,只要不定的參數個數能夠根據第一個后者后續的明確的參數確定下來,就可以使用不定參數,例如對于CRT中的sprintf函數,定義為:
int sprintf(char* buffer,const char* format,...)
由于所有的不定參數都可以通過format確定,因此使用不定個數的參數是沒有問題的。
轉載于:https://www.cnblogs.com/viviancc/archive/2012/10/22/2733768.html
總結
以上是生活随笔為你收集整理的__cdecl __stdcall区别-转的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网页数据分析工具
- 下一篇: 解析nginx负载均衡