在70年代以前,Compiler產生Object檔時
符號名稱和對應的變數、函式名稱是一樣的
也就是說如果你在code宣告定義一個foo函式
foo在Object檔中對應的符號名稱也叫做foo
這樣的設計有個缺點,如果你使用C Library
你就不能使用C Library已經定義好的變數與函式名稱
否則有重複定義的問題
因此後來UNIX就規定code經過編譯之後
要在對應的符號名稱前加入"_"
比如說"foo"就變成"_foo"
這個方法的確可以減少符號衝突的機率
但是並沒辦法完全解決符號衝突的問題
因此C++加入了Namespace來解決符號衝突的問題
C++有繼承和多載等特性
以多載來說,要讓Compiler能分辨兩個相同名稱的函式
就必須要在產生的符號上動一些手腳
這個動作叫做Name Mangling
以下列這個例子來說
int func(int); float func(float); class C { int func(int); class C2 { int func(int); }; }; namespace N { int func(int); class C { int func(int); }; }
這段code一共有6個func函式,他們不同的地方只有參數以及Namespace不一樣
Compiler會使用這些函式的Function Signature來辨別他們
Function Signature會加入函式所在Class以及Namespace等資訊
用來辨別不同的函式,就好像函式的簽名一樣
例如:
class C { Function Signature
int func(int); ==> int C::func(int)
class C2 {
int func(int); ==> int C::C2::func(int)
};
};
namespace N {
int func(int); ==> int N::func(int)
class C {
int func(int); ==> int N::C::func(int)
};
}
當compiler在把source code編譯成object檔的時候
會使用Name Mangling這個方法,把Function Signature修飾後轉成對應的符號
例如(使用GCC):
修飾過後的符號名稱
int C::C2::func(int) ==> _ZN1C2C24funcEi
int N::C::func(int) ==> _ZN1N1C4funcEi
int func(int) ==> _Z4funci
float func(float) ==> _Z4funcf
GCC的C++名稱修飾規則如下
1.所有的符號都由"_Z"開頭
2.如果是巢狀的名稱,就在開頭的"_Z"之後加上"N"
3.接下來是Namespace以及Class的名稱,寫法為字串長度 + "N" or "C"
4.如果是巢狀名稱的話,最後以"E"結尾
5.E後面接retrun type,int寫為i,float寫為f
此外binutils當中提供一個工具"c++filt"可以幫我們解析修飾過得名稱
ex:
$ c++filt _ZN1N1C4funcEi N::C::func(int)C++的global varible以及static varible同樣也會經過修飾
例如Namespace foo中的global varible bar會被修飾成 _ZN3foo3barE
不同編譯器Name Mangling的作法通常會不一樣
例如Visual C++就使用和GCC完全不同的Name Mangling方法
Reference: http://en.wikipedia.org/wiki/Name_mangling
程式設計師的自我修養:連結、載入、程式庫
沒有留言:
張貼留言