日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++调用约定

發布時間:2023/12/20 c/c++ 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++调用约定 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

<div class="markdown_views"><p>有一定C++開發經驗的人一定對”__cdecl、__stdcall、__fastcall”肯定不陌生吧!但你真正理解了嗎?是的,我曾在這采了無數個坑,栽了無數個跟頭,終于忍無可忍要把它總結一下(雖然我已經有能力解決大部分這種問題了)!</p>

什么是調用約定

函數的調用約定,顧名思義就是對函數調用的一個約束和規定(規范),描述了函數參數是怎么傳遞和由誰清除堆棧的。它決定以下內容:(1)函數參數的壓棧順序,(2)由調用者還是被調用者把參數彈出棧,(3)以及產生函數修飾名的方法。

我們知道函數由以下幾部分構成:返回值類型 函數名(參數列表),如:
【code1】

void function(); int add(int a, int b);

以上是大家所熟知的構成部分,其實函數的構成還有一部分,那就是調用約定。如下:
【code2】

void __cdecl function(); int __stdcall add(int a, int b);

上面的__cdecl和__stdcall就是調用約定,其中__cdecl是C和C++默認的調用約定,所以通常我們的代碼都如 【code1】中那樣定義,編譯器默認會為我們使用__cdecl調用約定。常見的調用約定有__cdecl、__stdcall、fastcall,應用最廣泛的是__cdecl和__stdcall,下面我們會詳細進行講述。。還有一些不常見的,如 __pascal、__thiscall、__vectorcall。

聲明和定義處調用約定必須要相同

在VC++中,調用約定是函數類型的一部分,因此函數的聲明和定義處調用約定要相同,不能只在聲明處有調用約定,而定義處沒有或與聲明不同。如下:
【code3】 錯誤的使用一:

int __stdcall add(int a, int b); int add(int a, int b) {return a + b; }

報錯:

error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__stdcall *)(int,int)’ to ‘int’

【code4】 錯誤的使用二:

int add(int a, int b); int __stdcall add(int a, int b) {return a + b; }

報錯:

error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__cdecl *)(int,int)’ to ‘int’

【code5】 錯誤的使用三:

int __stdcall add(int a, int b); int __cdecl add(int a, int b) {return a + b; }

報錯:

error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__stdcall *)(int,int)’ to ‘int’

【code6】 正確的用法:

int __stdcall add(int a, int b); int __stdcall add(int a, int b) {return a + b; }

函數的調用過程

要深入理解函數調用約定,你須要了解函數的調用過程和調用細節。
假設函數A調用函數B,我們稱A函數為”調用者”,B函數為“被調用者”。如下面的代碼,ShowResult為調用者,add為被調用者。

int add(int a, int b) {return a + b; }void ShowResult() {std::cout << add(5, 10) << std::endl; }

函數調用過程可以這么描述:
(1)先將調用者(A)的堆棧的基址(ebp)入棧,以保存之前任務的信息。
(2)然后將調用者(A)的棧頂指針(esp)的值賦給ebp,作為新的基址(即被調用者B的棧底)。
(3)然后在這個基址(被調用者B的棧底)上開辟(一般用sub指令)相應的空間用作被調用者B的棧空間。
(4)函數B返回后,從當前棧幀的ebp即恢復為調用者A的棧頂(esp),使棧頂恢復函數B被調用前的位置;然后調用者A再從恢復后的棧頂可彈出之前的ebp值(可以這么做是因為這個值在函數調用前一步被壓入堆棧)。這樣,ebp和esp就都恢復了調用函數B前的位置,也就是棧恢復函數B調用前的狀態。
這個過程在AT&T匯編中通過兩條指令完成,即:

leaveret這兩條指令更直白點就相當于:mov %ebp , %esppop %ebp

此部分內容參考:http://blog.csdn.net/zsy2020314/article/details/9429707

__cdecl的特點

__cdecl 是 C Declaration 的縮寫,表示 C 和 C++ 默認的函數調用約定。是C/C++和MFCX的默認調用約定。

  • 按從右至左的順序壓參數入棧、。
  • 由調用者把參數彈出棧。切記:對于傳送參數的內存棧是由調用者來維護的,返回值在EAX中。因此對于像printf這樣可變參數的函數必須用這種約定。
  • 編譯器在編譯的時候對這種調用規則的函數生成修飾名的時候,在輸出函數名前加上一個下劃線前綴,格式為_function。如函數int add(int a, int b)的修飾名是_add。

(1).為了驗證參數是從右至左的順序壓棧的,我們可以看下面這段代碼,Debug進行單步調試,可以看到我們的調用棧會先進入GetC(),再進入GetB(),最后進入GetA()。

(2).第二點“調用者把參數彈出棧”,這是編譯器的工作,暫時沒辦法驗證。要深入了解這部分,需要學習匯編語言相關的知識。

(3).函數的修飾名,這個可以通過對編譯出的dll使用VS的”dumpbin /exports ProjectName.dll”命令進行查看(后面章節會進行詳細介紹),或直接打開.obj文件查找對應的方法名(如搜索add)。

從代碼和程序調試的層面考慮,參數的壓棧順序和棧的清理我們都不用太觀注,因為這是編譯器的決定的,我們改變不了。但第三點卻常常困擾我們,因為如果不弄清楚這點,在多個庫之間(如dll、lib、exe)相互調用、依賴時常常出出現莫名其妙的錯誤。這個我在后面章節會進行詳細介紹。

__stdcall的特點

__stdcall是Standard Call的縮寫,是C++的標準調用方式,當然這是微軟定義的標準,__stdcall通常用于Win32 API中(可查看WINAPI的定義)。

  • 按從右至左的順序壓參數入棧。
  • 由被調用者把參數彈出棧。切記:函數自己在退出時清空堆棧,返回值在EAX中。
  • __stdcall調用約定在輸出函數名前加上一個下劃線前綴,后面加上一個“@”符號和其參數的字節數,格式為_function@number。如函數int sub(int a, int b)的修飾名是_sub@8。

__fastcall的特點

__fastcall調用的主要特點就是快,因為它是通過寄存器來傳送參數的。

  • 實際上__fastcall用ECX和EDX傳送前兩個DWORD或更小的參數,剩下的參數仍自右向左壓棧傳送,被調用的函數在返回前清理傳送參數的內存棧。
  • __fastcall調用約定在輸出函數名前加上一個“@”符號,后面也是一個“@”符號和其參數的字節數,格式為@function@number,如double multi(double a, double b)的修飾名是@multi@16。
  • __fastcall和__stdcall很象,唯一差別就是頭兩個參數通過寄存器傳送。注意通過寄存器傳送的兩個參數是從左向右的,即第1個參數進ECX,第2個進EDX,其他參數是從右向左的入棧,返回仍然通過EAX。

以上內容參考:http://www.3scard.com/index.php?m=blog&f=view&id=10

__thiscall

__thiscall是C++類成員函數缺省的調用約定,但它沒有顯示的聲明形式。因為在C++類中,成員函數調用還有一個this指針參數,因此必須特殊處理,thiscall調用約定的特點:

  • 參數入棧:參數從右向左入棧
  • this指針入棧:如果參數個數確定,this指針通過ecx傳遞給被調用者;如果參數個數不確定,this指針在所有參數壓棧后被壓入棧。
  • 棧恢復:對參數個數不定的,調用者清理棧,否則函數自己清理棧。

總結

這里主要總結一下_cdecl、_stdcall、__fastcall三者之間的區別:

要點__cdecl__stdcall__fastcall
參數傳遞方式右->左右->左左邊開始的兩個不大于4字節(DWORD)的參數分別放在ECX和EDX寄存器,其余的參數自右向左壓棧傳送
清理棧方調用者清理被調用函數清理被調用函數清理
適用場合C/C++、MFC的默認方式; 可變參數的時候使用;Win API要求速度快
C編譯修飾約定_functionname_functionname@number@functionname@number

本文章轉自:luoweifu
帶你玩轉Visual Studio——綁定進程調試

*



(function?()?{('pre.prettyprint code').each(function () {
var lines = (this).text().split(\n).length;varnumbering = $('
    ').addClass('pre-numbering').hide();
    (this).addClass(has?numbering).parent().append(numbering);
    for (i = 1; i <= lines; i++) {
    numbering.append(('
    • ').text(i));
      };
      $numbering.fadeIn(1700);
      });
      });

      歡迎使用Markdown編輯器寫博客

      本Markdown編輯器使用StackEdit修改而來,用它寫博客,將會帶來全新的體驗哦:

      • Markdown和擴展Markdown簡潔的語法
      • 代碼塊高亮
      • 圖片鏈接和圖片上傳
      • LaTex數學公式
      • UML序列圖和流程圖
      • 離線寫博客
      • 導入導出Markdown文件
      • 豐富的快捷鍵

      快捷鍵

      • 加粗 Ctrl + B
      • 斜體 Ctrl + I
      • 引用 Ctrl + Q
      • 插入鏈接 Ctrl + L
      • 插入代碼 Ctrl + K
      • 插入圖片 Ctrl + G
      • 提升標題 Ctrl + H
      • 有序列表 Ctrl + O
      • 無序列表 Ctrl + U
      • 橫線 Ctrl + R
      • 撤銷 Ctrl + Z
      • 重做 Ctrl + Y

      Markdown及擴展

      Markdown 是一種輕量級標記語言,它允許人們使用易讀易寫的純文本格式編寫文檔,然后轉換成格式豐富的HTML頁面。 —— [ 維基百科 ]

      使用簡單的符號標識不同的標題,將某些文字標記為粗體或者斜體,創建一個鏈接等,詳細語法參考幫助?。

      本編輯器支持 Markdown Extra ,  擴展了很多好用的功能。具體請參考Github.

      表格

      Markdown Extra 表格語法:

      項目價格
      Computer$1600
      Phone$12
      Pipe$1

      可以使用冒號來定義對齊方式:

      項目價格數量
      Computer1600 元5
      Phone12 元12
      Pipe1 元234

      定義列表

      Markdown Extra 定義列表語法:項目1項目2
      定義 A
      定義 B
      項目3
      定義 C

      定義 D

      定義D內容

      代碼塊

      代碼塊語法遵循標準markdown代碼,例如:

      @requires_authorization def somefunc(param1='', param2=0):'''A docstring'''if param1 > param2: # interestingprint 'Greater'return (param2 - param1 + 1) or None class SomeClass:pass >>> message = '''interpreter ... prompt'''

      腳注

      生成一個腳注1.

      目錄

      用 [TOC]來生成目錄:

      • 什么是調用約定
        • 聲明和定義處調用約定必須要相同
        • 函數的調用過程
      • __cdecl的特點
      • __stdcall的特點
      • __fastcall的特點
      • __thiscall
      • 總結
        • 快捷鍵
        • Markdown及擴展
          • 表格
          • 定義列表
          • 代碼塊
          • 腳注
          • 目錄
          • 數學公式
          • UML 圖
        • 離線寫博客
        • 瀏覽器兼容

      數學公式

      使用MathJax渲染LaTex 數學公式,詳見math.stackexchange.com.

      • 行內公式,數學公式為:Γ(n)=(n?1)!?nN
      • 塊級公式:

      x=?b±b2?4ac???????2a

      更多LaTex語法請參考 這兒.

      UML 圖:

      可以渲染序列圖:

      Created with Rapha?l 2.1.0張三張三李四李四嘿,小四兒, 寫博客了沒?李四愣了一下,說:忙得吐血,哪有時間寫。

      或者流程圖:

      Created with Rapha?l 2.1.0開始我的操作確認?結束yesno
      • 關于 序列圖 語法,參考 這兒,
      • 關于 流程圖 語法,參考 這兒.

      離線寫博客

      即使用戶在沒有網絡的情況下,也可以通過本編輯器離線寫博客(直接在曾經使用過的瀏覽器中輸入write.blog.csdn.net/mdeditor即可。Markdown編輯器使用瀏覽器離線存儲將內容保存在本地。

      用戶寫博客的過程中,內容實時保存在瀏覽器緩存中,在用戶關閉瀏覽器或者其它異常情況下,內容不會丟失。用戶再次打開瀏覽器時,會顯示上次用戶正在編輯的沒有發表的內容。

      博客發表后,本地緩存將被刪除。 

      用戶可以選擇 把正在寫的博客保存到服務器草稿箱,即使換瀏覽器或者清除緩存,內容也不會丟失。

      注意:雖然瀏覽器存儲大部分時候都比較可靠,但為了您的數據安全,在聯網后,請務必及時發表或者保存到服務器草稿箱

      瀏覽器兼容

    • 目前,本編輯器對Chrome瀏覽器支持最為完整。建議大家使用較新版本的Chrome。
    • IE9以下不支持
    • IE9,10,11存在以下問題
    • 不支持離線功能
    • IE9不支持文件導入導出
    • IE10不支持拖拽文件導入


    • 這里是 腳注 的 內容. ?

    總結

    以上是生活随笔為你收集整理的C++调用约定的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。