『壹』 C++函數重載的編譯後的一個奇怪的問題
是的,對於編譯器來說,名字是不一樣而且唯一的,編譯器會對函數名「修飾」。
C++ 編譯器的函數名修飾規則
函數名字修飾(Decorated Name)方式
函數的名字修飾(Decorated Name)就是編譯器在編譯期間創建的一個字元串,用來指明函數的定義或原型。LINK程序或其他工具有時需要指定函數的名字修飾來定位函數的正確位置。多數情況下程序員並不需要知道函數的名字修飾,LINK程序或其他工具會自動區分他們。當然,在某些情況下需要指定函數的名字修飾,例如在C++程序中,為了讓LINK程序或其他工具能夠匹配到正確的函數名字,就必須為重載函數和一些特殊的函數(如構造函數和析構函數)指定名字裝飾。另一種需要指定函數的名字修飾的情況是在匯編程序中調用C或C++的函數。如果函數名字,調用約定,返回值類型或函數參數有任何改變,原來的名字修飾就不再有效,必須指定新的名字修飾。C和C++程序的函數在內部使用不同的名字修飾方式,下面將分別介紹這兩種方式。
1.C編譯器的函數名修飾規則
對於__stdcall調用約定,編譯器和鏈接器會在輸出函數名前加上一個下劃線前綴,函數名後面加上一個「@」符號和其參數的位元組數,例如_functionname@number。__cdecl調用約定僅在輸出函數名前加上一個下劃線前綴,例如_functionname。__fastcall調用約定在輸出函數名前加上一個「@」符號,後面也是一個「@」符號和其參數的位元組數,例如@functionname@number
2.C++編譯器的函數名修飾規則
C++的函數名修飾規則有些復雜,但是信息更充分,通過分析修飾名不僅能夠知道函數的調用方式,返回值類型,參數個數甚至參數類型。不管__cdecl,__fastcall還是__stdcall調用方式,函數修飾都是以一個「?」開始,後面緊跟函數的名字,再後面是參數表的開始標識和按照參數類型代號拼出的參數表。對於__stdcall方式,參數表的開始標識是「@@YG」,對於__cdecl方式則是「@@YA」,對於__fastcall方式則是「@@YI」。參數表的拼寫代號如下所示:
X--void
D--char
E--unsigned char
F--short
H--int
I--unsigned int
J--long
K--unsigned long(DWORD)
M--float
N--double
_N--bool
U--struct
....
指針的方式有些特別,用PA表示指針,用PB表示const類型的指針。後面的代號表明指針類型,如果相同類型的指針連續出現,以「0」代替,一個「0」代表一次重復。U表示結構類型,通常後跟結構體的類型名,用「@@」表示結構類型名的結束。函數的返回值不作特殊處理,它的描述方式和函數參數一樣,緊跟著參數表的開始標志,也就是說,函數參數表的第一項實際上是表示函數的返回值類型。參數表後以「@Z」標識整個名字的結束,如果該函數無參數,則以「Z」標識結束。下面舉兩個例子,假如有以下函數聲明:
int Function1 (char *var1,unsigned long);
其函數修飾名為「?Function1@@YGHPADK@Z」,而對於函數聲明:
void Function2();
其函數修飾名則為「?Function2@@YGXXZ」 。
對於C++的類成員函數(其調用方式是thiscall),函數的名字修飾與非成員的C++函數稍有不同,首先就是在函數名字和參數表之間插入以「@」字元引導的類名;其次是參數表的開始標識不同,公有(public)成員函數的標識是「@@QAE」,保護(protected)成員函數的標識是「@@IAE」,私有(private)成員函數的標識是「@@AAE」,如果函數聲明使用了const關鍵字,則相應的標識應分別為「@@QBE」,「@@IBE」和「@@ABE」。如果參數類型是類實例的引用,則使用「AAV1」,對於const類型的引用,則使用「ABV1」。下面就以類CTest為例說明C++成員函數的名字修飾規則:
classCTest
{
......
private:
voidFunction(int);
protected:
voidCopyInfo(constCTest&src);
public:
longDrawText(HDChdc,longpos,constTCHAR*text,RGBQUADcolor,BYTEbUnder,boolbSet);
longInsightClass(DWORDdwClass)const;
......
};
對於成員函數Function,其函數修飾名為「?Function@CTest@@AAEXH@Z」,字元串「@@AAE」表示這是一個私有函數。成員函數CopyInfo只有一個參數,是對類CTest的const引用參數,其函數修飾名為「?CopyInfo@CTest@@IAEXABV1@@Z」。DrawText是一個比較復雜的函數聲明,不僅有字元串參數,還有結構體參數和HDC句柄參數,需要指出的是HDC實際上是一個HDC__結構類型的指針,這個參數的表示就是「PAUHDC__@@」,其完整的函數修飾名為「?DrawText@CTest@@QAEJPAUHDC__@@JPBDUtagRGBQUAD@@E_N@Z」。InsightClass是一個共有的const函數,它的成員函數標識是「@@QBE」,完整的修飾名就是「?InsightClass@CTest@@QBEJK@Z」。
『貳』 c++中的_T是什麼意思
在c++中是引用,直接使用你輸入的參數,T就是你輸入參數的另外一個名字
『叄』 編譯器筆記34-中間代碼生成-布爾表達式的回填
基本思想:生成一個跳轉指令時,暫時不指定該跳轉指令的目標標號。這樣的指令都被放入由跳轉指令組成的列表中。同一個列表中的所有跳轉指令具有相同的目標標號。等到能夠確定正確的目標標號時,才去填充這些指令的目標標號。
B.truelist:指向一個包含跳轉指令的列表,這些指令最終獲得的目標標號就是當B為真時控制流應該轉向的指令的標號。
B.falselist:指向一個包含跳轉指令的列表,這些指令最終獲得的目標標號就是當B為假時控制流應該轉向的指令的標號。
為了處理衫芹跳轉指令的列表我們構造了三個函數
上述的布爾表達式將被翻譯成兩條跳轉指令。兩條跳轉指令的標號都不填寫因為這兩條跳轉指令的標號都在等待回填,因此我們要把它放到相應的列表中。
第一條跳轉指令的目標標號是B的真出口,因此我們把它放到B.truelist中。調用makelist函數生成一個只包含nextquad的列表,並把這個列表的指針賦值給truelist,這里的nextquad是指即將生成的下一條指令的標號或瞎畢,即gen('if' E1.addr relop E2.addr'goto_')這條指令的標號。
第二條跳轉指令的目標標號是B的假出口,因此把這條跳轉指令存放到B.falselist中。因此我們調用makelist函數生成一個只包含nextquad+1這樣一個標號的列表,nextquad+1標號就是gen('goto_')這條指令的標號。
這樣的話我們就將這兩條指令分別放入到B.truelist和B.falselist中。
問:list中存的是對應跳轉指令的標號?
當B定義為true時,此時可以確定布爾表達式的值為真,生成一條跳轉到B的真出口的一條指令。由於此真出口的標號不能確定有待回填,我們把它放入到B.truelist中。
當B定義為false時,此時可以確定布爾表達式的值為假,生成一條跳轉到B的假出口的一條指令。由於此真出口的標號不能確定有待回填,我們把它放入到B.falselist中。
對B的翻譯與其對應的子表達式B1的翻譯是相同的,因此B的屬性值等於B1的屬性值。
B的值與B1的值正好相反,因此將兩個非終結符的屬性進行對調。
B1.truelist中的這些指令都要跳轉到B1的真出口,當B1為真的時候整個表達式的值就是為真的,因此B1的真出口就是B的真出口。要跳轉到B1的真出口就是跳轉到B的真出口,因此B1.truelist中的指令都要放到B.truelist中。
B2.truelist的指令都要跳轉到真出口,當B2為真時整個表達式的值也為真,因此B2的真出口就是B的真出口。要跳轉到B2的真出口就是要跳轉到B的真出口,因此B2.truelist中的指令都要放到B.truelist中。
B1.falselist中的指令它們都是要跳轉到B1的假出口,當B1的值為假的時候我們要進一步判斷B2的值,因此B1的假出口就是B2的第一條指令,因此B1.falselist中的指令都要跳轉到B2的第一條指令。
B2.falselist中的指令都要跳轉到B2的假出口,當B2的值為假的時候那麼整個布爾表達式的值也是假的。因此B2的假出口就是B的假出口,要跳轉到B2的假出口也就是要跳轉到B的假出口。B2.falselist中的指令都要放置到B.falselist中。
根據此示意神前圖可以看出,在分析B2之前,要用B2的第一條指令的標號來回填B1.falselist中的各條指令。當然我們可以記錄下B2的第一條指令的標號在歸約時完成此回填動作。為了記下B2第一條指令的標號我們在非終結符B2之前插入一個標記非終結符M。與M關聯的語義動作它的任務就是記錄下B2的第一條語義動作的標號。我們給M設置一個綜合屬性quad,M.quad等於下一條指令的標號。因為我們把M放在B2之前,因此M.quad記錄的是第二條指令的標號。根據翻譯方案示意圖,我們要用M.quad來回填B1.falselist中的各條指令,因此調用backpatch用M.quad回填B1.falselist中的各條屬性。B.truelist是由B1.truelist和B2.truelist合並而成的,因此我們調用merge函數將B1.truelist和B2.truelist進行合並,將合並後的指針賦值給B.truelist。
註:因為這里我們定義的都是綜合屬性,從左向右掃描輸入串。makelist函數生成一個只包含下一條指令的列表,並把指針賦值給B.trulist。我們假設下一條指令從100開始。gen(『if 』 E 1 .addr relop E 2 .addr 『goto _』)中E1.address等於a,relop就是小於號,E2.address等於b,引號中的字元串按字面值傳遞。下劃線表示待回填的目標標號。
註:將棧頂中的空串歸約成一個標記非終結符M
註:有四條指令是等待回填的,在B的truelist中有兩條指令100和104,當B的真出口確定以後我們將用B的真出口的標號這兩條指令。同理當B的假出口確定以後將會用B的假出口的標號回填此兩條指令。