程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++函數調用方式解析

C++函數調用方式解析

編輯:C++入門知識

在C++語言中進行C++函數調用時,C++中的虛函數的作用主要是實現了多態的機制,虛函數是通過一張虛函數表來實現的,在C++中可以確定每個函數都有特定的目的。

在CPU中,計算機沒有辦法知道一個函數調用需要多少個、什麼樣的參數,也沒有硬件可以保存這些參數。也就是說,計算機不知道怎麼給這個函數傳遞參數,傳遞參數的工作必須由C++函數調用者和函數本身來協調。為此,計算機提供了一種被稱為棧的數據結構來支持參數傳遞。

棧是一種先進後出的數據結構,棧有一個存儲區、一個棧頂指針。棧頂指針指向堆棧中第一個可用的數據項被稱為棧頂)。用戶可以在棧頂上方向棧中加入數據,這個操作被稱為壓棧(Push),壓棧以後,棧頂自動變成新加入數據項的位置,棧頂指針也隨之修改。用戶也可以從堆棧中取走棧頂,稱為彈出棧(pop),彈出棧後,棧頂下的一個元素變成棧頂,棧頂指針隨之修改。

C++函數調用時,調用者依次把參數壓棧,然後調用函數函數調用以後,在堆棧中取得數據,並進行計算。函數計算結束以後,或者調用者、或者函數本身修改堆棧,使堆棧恢復原裝。在參數傳遞中,有兩個很重要的問題必須得到明確說明: 當參數個數多於一個時,按照什麼順序把參數壓入堆棧 函數調用後,由誰來把堆棧恢復原裝

在高級語言中,通過函數調用約定來說明這兩個問題。常見的調用約定有stdcall很多時候被稱為pascal調用約定,因為pascal是早期很常見的一種教學用計算機程序設計語言,其語法嚴謹,使用的C++函數調用約定就是stdcall。在Microsoft C++系列的C/C++編譯器中,常常用PASCAL宏來聲明這個調用約定,類似的宏還有WINAPI和CALLBACK。

stdcall調用約定聲明的語法為(以前文的那個函數為例):nt __stdcall function(int a,int b)stdcall的調用約定意味著:1)參數從右向左壓入堆棧,2)函數自身修改堆棧 3)函數名自動加前導的下劃線,後面緊跟一個@符號,其後緊跟著參數的尺寸。

  • 更快更好的制定C++函數參數
  • 如何對C++進行函數重載
  • 處理C++靜態成員時的注意事項
  • 如何對C++虛基類構造函數
  • 輕輕松松解決C++異常問題處理

以上述這個函數為例,參數b首先被壓棧,然後是參數a,函數調用function(1,2)調用處翻譯成匯編語言將變成:push 2 第二個參數入棧push 1 第一個參數入棧call function 調用參數,注意此時自動把cs:eip入棧而對於函數自身,則可以翻譯為。

  • 更快更好的制定C++函數參數
  • 如何對C++進行函數重載
  • 處理C++靜態成員時的注意事項
  • 如何對C++虛基類構造函數
  • 輕輕松松解決C++異常問題處理

push ebp 保存ebp寄存器,該寄存器將用來保存堆棧的棧頂指針,可以在函數退出時恢復mov ebp,esp 保存堆棧指針mov eax,[ebp + 8H] 堆棧中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向aadd eax,[ebp + 0CH] 堆棧中ebp + 12處保存了bmov esp,ebp 恢復esppop ebpret 8

注意不同編譯器會插入自己的匯編代碼以提供編譯的通用性,但是大體代碼如此。其中在函數開始處保留esp到ebp中,在函數結束恢復是編譯器常用的方法。

C++函數調用看,2和1依次被push進堆棧,而在函數中又通過相對於ebp(即剛進函數時的堆棧指針)的偏移量存取參數。函數結束後,ret 8表示清理8個字節的堆棧,函數自己恢復了堆棧。

由於調用者沒有理解WINAPI的含義錯誤的增加了這個修飾,上述代碼必然導致堆棧被破壞,MFC在編譯時插入的checkesp函數將告訴你,堆棧被破壞了如果定義的約定和使用的約定不一致,則將導致堆棧被破壞,導致嚴重問題,下面是兩種常見的問題:

  1. 函數原型聲明和函數體定義不一致
  2. DLL導入函數時聲明了不同的函數約定

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved