1. 程式人生 > >名字修飾約定和函式呼叫約定

名字修飾約定和函式呼叫約定

所謂名字修飾約定,就是指變數名、函式名等經過編譯後重新輸出名稱的規則。

  比如原始碼中函式名稱為int Func(int a,int b),經過編譯後名稱可能為[email protected]@[email protected][email protected]@[email protected][email protected],也有可能與原始碼中名稱相同為Func。

  影響編譯後輸出的名稱通常與名字修飾約定(extern "C"、extern "C++"等)和函式呼叫約定(__stdcall、__cdecl等)等相關。

  口說千遍,不如實際演練一遍。那麼,就讓我們寫程式碼來測試下。

  注意,本文只討論extern "C"、extern "C++"和__stdcall、__cdecl相關的約定,其他約定不在本文討論範圍內。另外,編譯的環境為XP + VC++6.0SP6。

  首先,用C方式匯出兩個函式:

Dll1.c

_declspec(dllexport) int __cdecl Func_cdecl(int a,int b)
{
	return 1;
}

_declspec(dllexport) int __stdcall Func_stdcall(int a,int b)
{
	return 1;
}

匯出的兩個函式名為:


再以C++方式匯出:

  Dll1.cpp

_declspec(dllexport) int __cdecl Func_cdecl(int a,int b)
{
	return 1;
}

_declspec(dllexport) int __stdcall Func_stdcall(int a,int b)
{
	return 1;
}
匯出結果如下:

然後,我們再以C++方式匯出如下程式碼中的函式:

extern "C" _declspec(dllexport) int __stdcall Func_C_stdcall(int a,int b)
{
	return 1;
}

extern "C++" _declspec(dllexport) int __stdcall Func_CPP_stdcall(int a,int b)
{
	return 1;
}

extern "C" _declspec(dllexport) int __cdecl Func_C_cdecl(int a,int b)
{
	return 1;
}

extern "C++" _declspec(dllexport) int __cdecl Func_CPP_cdecl(int a,int b)
{
	return 1;
}

匯出結果如下


有了以上實驗結果,我們再結合以下名字輸出規則進行理解:

  1. C方式編譯(extern "C"):
    1. __stdcall呼叫約定:輸出名稱在原名稱前加一下劃線,後面再加上一個“@”和其引數的總位元組數(_原名稱@引數總位元組數),如名稱int Func_C_stdcall(int a,int b)輸出為[email protected]
    2. __cdecl呼叫約定:與原名稱相同,如名稱int Func_C_cdecl(int a,int b)輸出還是為Func_C_cdecl;
  2. C++方式編譯(extern "C++"):
    1. __stdcall呼叫約定:
      1. 輸出名稱以“?”開始,後跟原名稱;
      2. 原名稱後再跟“@@YG”,後面再跟返回值代號和引數表代號,代號表示如下:
        X--void ,
        D--char,
        E--unsigned char,
        F--short,
        H--int,
        I--unsigned int,
        J--long,
        K--unsigned long,
        M--float,
        N--double,
        _N--bool,
        ...
        PA--表示指標,後面的代號表明指標型別,如果相同型別的指標連續出現,以“0”代替,一個“0”代表一次重複;
      3. 引數表後以“@Z”標識整個名字的結束,如果該函式無引數,則以“Z”標識結束。如名稱int Func_CPP_stdcall(int a,int b)編譯後的輸出名稱為[email protected]@[email protected]
    2. __cdecl呼叫約定:與_stdcall呼叫約定基本一致,只是引數表的開始標識由上面的“@@YG”變為“@@YA”。如名稱int Func_CPP_cdecl(int a,int b)編譯後輸出名稱為[email protected]@[email protected]

  有個這個規則,再回頭去看我們的實驗結果,就很好理解了。

  當然,編譯C檔案和編譯CPP檔案,不需加extern "C"和extern "C++",因為編譯C檔案當然預設的是extern "C",而編譯CPP檔案則預設的是extern "C++"。

  現在我們也能理解為什麼匯出DLL時通常需要加上extern "C"。試想,如果一個C++匯出的dll,沒有加extern "C",則匯出的名稱為extern "C++"約定下的名稱。如果這個dll需要提供給用C編寫的程式使用,那麼這個程式是無法呼叫這個dll的,因為C寫的程式遵循的是extern "C"約定,連結時連結器將按照extern "C"約定的名稱去尋找外部名稱,這當然找不到,因為dll中的輸出名稱為extern "C++"約定下的名稱。


相關推薦

名字修飾約定函式呼叫約定

所謂名字修飾約定,就是指變數名、函式名等經過編譯後重新輸出名稱的規則。   比如原始碼中函式名稱為int Func(int a,int b),經過編譯後名稱可能為[email protected]@[email protected]、[email&#

函式呼叫約定函式名稱修飾規則(一)

    作者:星軌(oRbIt)    E_Mail:[email protected]    轉載請註明原作者,否則請勿轉載       使用C/C++語言開發軟體的程式設計師經常碰到這樣的問題:有時候是程式編譯沒有問題,但是連結的時候總是報告函式不存在(經典的L

函式呼叫約定函式修飾規則

函式呼叫約定:是指當一個函式被呼叫時,函式的引數會被傳遞給被呼叫的函式和返回值會被返回給呼叫函式。函式的呼叫約定就是描述引數是怎麼傳遞和由誰平衡堆疊的,當然還有返回值。 幾種型別:__stdcall,__cdecl,__fastcall,__thiscall,__n

2018/10/03-函式呼叫約定、cdecl、stdcall、fastcall- 《惡意程式碼分析實戰》

  cdecl是最常用的約定之一,引數是從右到左按序被壓入棧,當函式完成時由呼叫者清理棧,並且將返回值儲存在EAX中。   stdcall約定是被呼叫函式負責清理棧,其他和cdecl非常類似。   fastcall呼叫約定跨編譯器時變化最多,但是它總體上在所有情況下的工作方式都是相似的。在fastcall

x64函式呼叫約定——MSVC & GCC

傳參 MSVC 前4個引數使用rcx、rdx、r8、r9,剩下的引數用棧 GCC 前6個引數使用rdi、rsi、rdx、rcx、r8、r9,剩下的引數用棧。注意rdx、rcx的順序和MSVC上不一樣 caller saved registers 這類暫存器可由子函式自由使用,

從彙編角度檢視C語言函式呼叫約定【非常有用】

轉自:https://blog.csdn.net/Holmofy/article/details/76094986   為了防止出現不必要的程式碼影響組合語言的檢視,所以程式中不使用任何庫函式,以保持彙編程式碼的簡潔。 這裡所使用的彙編是VC的MASM。 預設函式呼叫方式_

cdecl、stdcall、fastcall、thiscall函式呼叫約定區別 (轉)

 在C語言中,假設我們有這樣的一個函式:    int function(int a,int b)    呼叫時只要用result = function(1,2)這樣的方式就可以使用這個函式。但是,當高階語言被編譯成計算機可以識別的機器碼時,有一個問題就凸現出來:在CPU中,計算機沒有辦法知道一個函式呼叫需要

cdecl、stdcall、fastcall函式呼叫約定區別

論函式呼叫約定   在C語言中,假設我們有這樣的一個函式:      int function(int a,int b)      呼叫時只要用result = function(1,2)這樣的方式就可以使用這個函式。但是,當高階語言被編譯成計算機可以識別的機器碼時,有一個問題就凸現出來:在CPU中

【軟體開發底層知識修煉】二十五 ABI之函式呼叫約定二之函式返回值為結構體時的約定

上一篇文章學習了幾種函式呼叫約定的區別,點選連結檢視上一篇文章:【軟體開發底層知識修煉】二十四 ABI之函式呼叫約定 本篇文章繼續學習函式呼叫約定中,關於函式返回值的問題。當函式返回值為結構體時,函式返回值是如何來傳給呼叫者的。

【軟體開發底層知識修煉】二十四 ABI之函式呼叫約定

上一篇文章學習了Linux環境下的函式棧幀的形成與摧毀。點選連結檢視相關文章:軟體開發底層知識修煉】二十三 ABI-應用程式二進位制介面三之深入理解函式棧幀的形成與摧毀 本篇文章繼續學習ABI介面相關的內容。函式呼叫約定

常見函式呼叫約定(x86、x64、arm、arm64)

我學習逆向,整理的一些常見的函式呼叫約定反彙編筆記。由於我是新手,肯定有一些疏漏不完善的,我遇到了會實時更新的。 X86 函式呼叫約定 X86 有三種常用呼叫約定,cdecl(C規範)/stdcall(WinAPI預設)/fastcall

x86 常見呼叫約定(cdecl,fastcall,stdcall) & x86ARM呼叫約定的棧幀分析 & ARM ATPCS(ARM-THUMB procedure call standard)

#PS:要轉載請註明出處,本人版權所有 #PS:這個只是 《 我自己 》理解,如果和你的 #原則相沖突,請諒解,勿噴 由於某些工作的需要,我需要掌握X86以及ARM的一些呼叫規則,讓自己可以大致看懂ASM程式碼。於是,我總結了一下我需要的東西

函式呼叫約定

在C語言中,假設我們有這樣的一個函式: int function(int a,int b) 呼叫時只要用result = function(1,2)這樣的方式就可以使用這個函式。但是,當高階語言被編譯成計算機可以識別的機器碼時,有一個問題就凸現出來:在CPU中,計算機沒有辦法

ARM函式呼叫約定

1、函式呼叫約定主要涉及引數如何傳遞、返回值如何傳遞、返回地址如何儲存以及不要破壞呼叫函式的上下文。那麼在ARM中,這些約定規則是什麼樣呢? 2、測試程式如下: static int fun_a(uint32_t a, uint32_t b, uint32_t c) {

Windows x64彙編函式呼叫約定

最近在寫一些字串函式的優化,用到x64彙編,我也是第一次接觸,故跟大家分享一下。 x86:又名 x32 ,表示 Intel x86 架構,即 Intel 的32位 80386 彙編指令集。 x64:表示 AMD64 和 Intel 的 EM64T ,而不包括 IA6

push、pop及函式呼叫約定

push: 把一個32位的運算元壓入堆疊中。這個操作導致esp被減4。esp被形象地稱為棧頂。我們認為頂部是地址小的區域,那麼,壓入堆疊中資料越多,這個堆疊也就越堆越高,esp也就越來越小。在32位平臺,esp每次減少4位元組。 pop: 相反,esp被加4,一個數據

c理解提高(3)程式的記憶體四區模型函式呼叫模型

程式的記憶體四區模型 記憶體四區的建立流程 流程說明 1、作業系統把物理硬碟程式碼load到記憶體 2、作業系統把c程式碼分成四個區 3、作業系統找到main函式入口執行   各區元素分析 函式呼叫模型 基本原理

函式引用函式呼叫的區別

我們現在來理解下函式引用和函式呼叫的本質區別:當引用函式時候,多個變數記憶體儲存的是函式的相同的入口指標,因此對於同一個函式來講,無論多少個變數引用,他們都是相等的,因為對於引用型別(物件,陣列,函式等)都是比較的是記憶體地址,如果他們記憶體地址一樣的話,說明是相同的;但是對於函式呼叫來講,比如程式碼三;每次

react 父子元件之間的通訊函式呼叫

reactjs是一枚新進小鮮肉,跟gulp搭配流行一段時間了。工作或者面試中經常遇到這樣的問題,“子元件如何向父元件傳值?”。其實很簡單,概括起來就是:react中state改變了,元件才會update。父寫好state和處理該state的函式,同時將函式名通過props屬性值的形式傳入子,子呼叫父的函式

巨集定義函式呼叫的區別

含引數的巨集與函式的區別 ?         巨集替換不佔執行時間,只佔編譯時間;而函式呼叫則佔執行時間(分配單元、保留現場、值傳遞、返回),所以每次執行都要載入所以執行起來比較慢一些。。 ?         定義巨集的時候不要在巨集及其引數之間鍵入空格,因為巨集替換的時候會把你不經意打的空格當作巨集的一部