最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C/C++函數(shù)的調(diào)用約定的使用

 更新時間:2022年06月23日 15:09:27   作者:dvlinker  
本文主要介紹了C/C++函數(shù)的調(diào)用約定的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

 函數(shù)的調(diào)用約定其實(shí)比較簡單,并不復(fù)雜,但很多人對這一塊內(nèi)容不太了解,甚至連工作幾年的朋友也不太清楚。最近有朋友想了解這一塊的內(nèi)容,所以今天我們就來講一下C/C++函數(shù)調(diào)用約定相關(guān)的內(nèi)容。

1、概述

常見的函數(shù)調(diào)用約定有__cdecl C調(diào)用、__stdcall標(biāo)準(zhǔn)調(diào)用、__fastcall快速調(diào)用以及__pascal調(diào)用:

這些調(diào)用是開發(fā)語言中的關(guān)鍵字,放置在函數(shù)前,用來指定函數(shù)的調(diào)用約定,比如:

BOOL __stdcall InitSDK();

如上所示,調(diào)用約定關(guān)鍵字一般放于返回值類型與函數(shù)之間。而函數(shù)返回值類型前面一般放置函數(shù)的導(dǎo)入導(dǎo)出聲明:(dll庫的函數(shù)接口有導(dǎo)入導(dǎo)出之分)

// 定義導(dǎo)出導(dǎo)入SDK_DLL_API宏
#ifdef DLL_EXPORTS
#define SDK_DLL_API _declspec(dllexport)
#else
#define SDK_DLL_API _declspec(dllimport)
#endif
 
// 將導(dǎo)出導(dǎo)入SDK_DLL_API宏放到返回值類型之前
SDK_DLL_API BOOL __stdcall InitSDK();

函數(shù)的調(diào)用約定主要決定三方面的內(nèi)容:

1)函數(shù)參數(shù)的入棧順序

函數(shù)調(diào)用時主調(diào)函數(shù)的參數(shù)是通過棧傳遞給被調(diào)用函數(shù)的。從匯編上看的比較清晰,在call函數(shù)之前,會將參數(shù)的值壓到棧上,比如:

如果函數(shù)有多個參數(shù),則會有兩種入棧方式,一種是從右到左依次入棧,一種是從左到右依次入棧,這是函數(shù)調(diào)用約定決定的。

2)參數(shù)??臻g由誰來釋放

函數(shù)調(diào)用完成后傳遞給被調(diào)用函數(shù)的參數(shù)的占用的棧空間是需要釋放掉的,專業(yè)術(shù)語叫“平棧”,清理掉參數(shù)的??臻g才能做到棧平衡。參數(shù)占用的??臻g到底是誰來清理,也是函數(shù)調(diào)用約定決定的。編譯器在編譯鏈接生成匯編代碼時,就生成好了清理參數(shù)??臻g的匯編代碼。

3)編譯時的函數(shù)名稱改編

不同的調(diào)用約定下編譯生成的函數(shù)名稱格式可能是不同的。C++之所以支持函數(shù)重載(源代碼中,函數(shù)名稱相同,函數(shù)參數(shù)不同),就是因?yàn)镃++編譯器會對函數(shù)名稱進(jìn)行改編,改編后的名稱中包含參數(shù)類型進(jìn)而能區(qū)分出重載的函數(shù)。

2、常見的調(diào)用約定說明

常見的函數(shù)調(diào)用約定有__cdecl C調(diào)用、__stdcall標(biāo)準(zhǔn)調(diào)用、__fastcall快速調(diào)用以及__pascal調(diào)用。C/C++ 中主要使用__cdecl C調(diào)用、__stdcall標(biāo)準(zhǔn)調(diào)用、__fastcall快速調(diào)用三種。__pascal 是用于 Pascal / Delphi 編程語言的調(diào)用規(guī)則,C/C++ 中也可以使用這種調(diào)用規(guī)則,但該調(diào)用約定已經(jīng)被C++廢棄,不提倡使用了。

下面我們來看看這幾種調(diào)用約定的異同點(diǎn),見下面的表格:

2.1、__cdecl C調(diào)用

它是C/C++函數(shù)默認(rèn)的調(diào)用規(guī)范,C/C++運(yùn)行時庫中的函數(shù)基本都是__cdecl調(diào)用。在該調(diào)用約定下,參數(shù)從右向左依次壓入棧中,由主調(diào)函數(shù)負(fù)責(zé)清理參數(shù)的??臻g。該調(diào)用約定適用于支持可變參數(shù)的函數(shù),因?yàn)橹挥兄髡{(diào)函數(shù)才知道給該種函數(shù)傳遞了多少個參數(shù),才知道應(yīng)該清理多少??臻g。比如支持可變參數(shù)的C函數(shù)printf:

int __cdecl printf ( const char *format, ... )
{
    va_list arglist;
    int buffing;
    int retval;
 
    _VALIDATE_RETURN( (format != NULL), EINVAL, -1);
 
    va_start(arglist, format);
 
    _lock_str2(1, stdout);
 
    __try {
        buffing = _stbuf(stdout);
 
        retval = _output_l(stdout,format,NULL,arglist);
 
        _ftbuf(buffing, stdout);
 
    }
    __finally {
        _unlock_str2(1, stdout);
    }
 
    return(retval);
}

2.2、__stdcall標(biāo)準(zhǔn)調(diào)用

它是Windows系統(tǒng)提供的系統(tǒng)API函數(shù)的調(diào)用約定,比如API函數(shù)GetWindowText的聲明如下:

WINUSERAPI
int
WINAPI
GetWindowTextW(
    _In_ HWND hWnd,
    _Out_writes_(nMaxCount) LPWSTR lpString,
    _In_ int nMaxCount);

其中,WINAPI宏就是__stdcall標(biāo)準(zhǔn)調(diào)用,即:

#define WINAPI __stdcall

同時__stdcall也是很多提供給第三方使用的SDK庫的API接口的調(diào)用約定。在該調(diào)用約定下,參數(shù)從右向左依次壓入棧中,由被調(diào)用函數(shù)負(fù)責(zé)清理?xiàng)?臻g。如果函數(shù)是可變參的,函數(shù)的調(diào)用約定會自動轉(zhuǎn)化為__cdecl調(diào)用。

2.3、__fastcall快速調(diào)用

該調(diào)用約定之所以被稱作為快速調(diào)用,因?yàn)橛胁糠謪?shù)可以通過寄存器直接傳遞,效率比較高。對于內(nèi)存大小小于等于4字節(jié)的參數(shù),直接使用ECX和EDX寄存器傳遞,剩余的參數(shù)則依次從右到左壓入棧中通過棧傳遞,參數(shù)傳遞占用的??臻g由被調(diào)用函數(shù)清理。

2.4、__thiscall調(diào)用

__thiscall是C++中的非靜態(tài)類成員函數(shù)的默認(rèn)調(diào)用約定。該調(diào)用約定也用到了寄存器傳參,在調(diào)用C++類的非靜態(tài)成員函數(shù)時會傳入當(dāng)前類對象的地址,該地址通過ECX寄存器來傳遞的。在該調(diào)用約定下,函數(shù)的參數(shù)按照從右到左的順序入棧,被調(diào)用的函數(shù)在返回前清理參數(shù)的??臻g。

3、調(diào)用約定不一致導(dǎo)致的軟件異常問題

以前我們將C++開發(fā)的SDK庫提供給第三方廠商做二次開發(fā),第三方客戶使用的是C#語言,即C#開發(fā)的程序去調(diào)用C++開發(fā)的SDK庫,當(dāng)時因?yàn)镾DK頭文件中聲明的回調(diào)函數(shù)沒有指定調(diào)用約定,導(dǎo)致程序出現(xiàn)異常崩潰的問題。

我們C++開發(fā)的SDK提供了設(shè)置消息回調(diào)的API接口,并給出了回調(diào)函數(shù)的聲明,如下:

/* 函數(shù)功能:用于消息回發(fā)的回調(diào)函數(shù)指針(服務(wù)器主動推送的消息通過該回調(diào)函數(shù)推給上層)
   參數(shù):DWORD dwMsgId:消息id 
         const unsigned char* pMsgBuf:消息中攜帶的數(shù)據(jù)buffer,buffer中的具體內(nèi)容取決于消息id,參看消息id的頭文件
                 DWORD dwMsgBufLen:消息中攜帶的數(shù)據(jù)buffer長度
   返回值:void
*/
typedef void (*PMsgCallBackFunc)( DWORD dwMsgId, const unsigned char* pMsgBuf, DWORD dwMsgBufLen );

設(shè)置回調(diào)函數(shù)的接口如下:

// 設(shè)置業(yè)務(wù)消息回調(diào)接口
SDK_DLL_API void __stdcall SetMsgCallBack( IN PMsgCallBackFunc pMsgCallBackFunc );

回調(diào)函數(shù)的實(shí)現(xiàn)在上層的C#程序中,回調(diào)函數(shù)的調(diào)用在C++實(shí)現(xiàn)的SDK中,因?yàn)榛卣{(diào)函數(shù)PMsgCallBackFunc在聲明時沒有指定函數(shù)調(diào)用約定,在C#程序中默認(rèn)是__stdcall標(biāo)準(zhǔn)約定,所以在C#中編譯時回調(diào)函數(shù)內(nèi)部會清理?xiàng)?臻g。而回調(diào)函數(shù)是在C++ SDK中調(diào)用的,在SDK編譯時默認(rèn)是__cdecl調(diào)用,會在調(diào)用回調(diào)函數(shù)處的主調(diào)函數(shù)中釋放??臻g,這樣導(dǎo)致回調(diào)函數(shù)調(diào)用后,主調(diào)函數(shù)會釋放一次棧空間,回調(diào)函數(shù)內(nèi)部會釋放一次??臻g,所以多釋放了一次參數(shù)??臻g,導(dǎo)致了棧不平衡,導(dǎo)致程序運(yùn)行出異常。

考慮跨語言調(diào)用的場景,SDK要提供標(biāo)準(zhǔn)的C接口。在SDK的頭文件中,SDK導(dǎo)出接口要指定調(diào)用約定,回調(diào)函數(shù)的聲明也要指定調(diào)用約定。

4、與調(diào)用約定相關(guān)的工程配置選項(xiàng)及/RTC編譯選項(xiàng)

在Visual Studio創(chuàng)建的C++工程中,在沒明確指定函數(shù)調(diào)用約定時,默認(rèn)使用的都是__cdecl調(diào)用,我們可以在工程屬性配置中看到:

對于C++工程,我們一般不需要修改默認(rèn)的調(diào)用約定。如果要指定dll庫導(dǎo)出接口的調(diào)用約定,我們也不需要修改工程配置,只需要在導(dǎo)出接口的頭文件的函數(shù)聲明處指定調(diào)用約定就可以了。

有人可能會說,工程屬性配置中使用了默認(rèn)的__cdecl調(diào)用,我們又在頭文件中將接口指定為__stdcall標(biāo)準(zhǔn)調(diào)用,會不會有沖突?到底以哪個為準(zhǔn)呢?沒有沖突的,編譯時是優(yōu)先以接口聲明處指定的調(diào)用約定為準(zhǔn)的。

在Debug下/RTC運(yùn)行時檢測編譯選項(xiàng)是默認(rèn)開啟的,/RTC運(yùn)行時檢測在函數(shù)調(diào)用完成后會去檢測棧是否平衡,關(guān)于這一點(diǎn)的說明如下:(MSDN上對/RTC編譯選項(xiàng)的說明) 

如果沒有釋放參數(shù)的??臻g或者參數(shù)??臻g多釋放了一次,都能檢測出來。如果檢測到,會彈出如下的提示:

 到此這篇關(guān)于C/C++函數(shù)的調(diào)用約定的使用的文章就介紹到這了,更多相關(guān)C/C++函數(shù)調(diào)用約定內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++實(shí)現(xiàn)簡單推箱子小游戲

    C++實(shí)現(xiàn)簡單推箱子小游戲

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡單推箱子小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • 新舊MFC版本實(shí)現(xiàn)CEdit透明的2種方法的實(shí)例代碼

    新舊MFC版本實(shí)現(xiàn)CEdit透明的2種方法的實(shí)例代碼

    新舊MFC版本實(shí)現(xiàn)CEdit透明的2種方法的實(shí)例代碼,需要的朋友可以參考一下
    2013-03-03
  • C++實(shí)現(xiàn)四叉樹效果(附源碼下載)

    C++實(shí)現(xiàn)四叉樹效果(附源碼下載)

    這篇文章主要介紹了C++實(shí)現(xiàn)四叉樹效果(附源碼下載),非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-03-03
  • 從匯編看c++中多態(tài)的應(yīng)用

    從匯編看c++中多態(tài)的應(yīng)用

    本篇文章是對c++中多態(tài)的應(yīng)用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • VS2022新建項(xiàng)目時沒有ASP.NET Web應(yīng)用程序(.NET Framework)

    VS2022新建項(xiàng)目時沒有ASP.NET Web應(yīng)用程序(.NET Framework)

    本文主要介紹了VS2022新建項(xiàng)目時沒有ASP.NET Web應(yīng)用程序的解決,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-10-10
  • 使用C語言實(shí)現(xiàn)內(nèi)存池的示例代碼

    使用C語言實(shí)現(xiàn)內(nèi)存池的示例代碼

    所謂內(nèi)存池,顧名思義和線程池的設(shè)計原理是一樣的,為了減少頻繁申請釋放內(nèi)存而帶來的資源消耗,減少釋放內(nèi)存后產(chǎn)生的內(nèi)存碎片,下面我們就來看看如何使用C語言實(shí)現(xiàn)內(nèi)存池吧
    2024-02-02
  • C++教程之變量的作用域與生命周期詳解

    C++教程之變量的作用域與生命周期詳解

    在C++編程中,變量的作用域和生命周期是非常重要的概念。了解這些概念可以幫助開發(fā)人員編寫更好的代碼并避免錯誤。在本文中,我們將探討C++中變量的作用域和生命周期,以及如何正確地使用它們
    2023-04-04
  • c++容器list、vector、map、set區(qū)別與用法詳解

    c++容器list、vector、map、set區(qū)別與用法詳解

    這篇文章主要介紹了c++容器list、vector、map、set區(qū)別與用法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • C++模擬實(shí)現(xiàn)vector流程詳解

    C++模擬實(shí)現(xiàn)vector流程詳解

    這篇文章主要介紹了C++容器Vector的模擬實(shí)現(xiàn),Vector是一個能夠存放任意類型的動態(tài)數(shù)組,有點(diǎn)類似數(shù)組,是一個連續(xù)地址空間,下文更多詳細(xì)內(nèi)容的介紹,需要的小伙伴可以參考一下
    2022-08-08
  • 一文詳解C語言操作符

    一文詳解C語言操作符

    這篇文章主要詳細(xì)介紹了C語言的操作符,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05

最新評論

崇左市| 安平县| 吉安市| 嘉义市| 广元市| 霍城县| 辽源市| 胶州市| 名山县| 永川市| 罗山县| 临城县| 红桥区| 万盛区| 铁岭县| 常宁市| 四平市| 宣城市| 马尔康县| 寻乌县| 绥滨县| 大关县| 牡丹江市| 宽城| 会宁县| 岳阳市| 张掖市| 桦南县| 定州市| 闽清县| 开原市| 信阳市| 奉化市| 二连浩特市| 翁源县| 天水市| 上思县| 旺苍县| 方城县| 江永县| 九龙坡区|