GNU C 的符號改編機制介紹

2021-06-28 11:28:55 字數 3388 閱讀 8731

眾所周知,強大的c++相較於c增添了許多功能。這其中就包括類、命名空間和過載這些特性。

對於類來說,不同類中可以定義名字相同的函式和變數,彼此不會相互干擾。命名空間可以保證在各個不同名字空間內的類、函式和變數名字不會互相影響。而過載可以保證即使在同乙個命名空間內的同乙個類中,函式名字也可以相同,只要引數不一樣就可以。

這樣的設計方便了程式開發者,不用擔心不同開發者都定義相同名字的函式的問題。但是,這也使得符號管理變得更為複雜。

對於在不同類中的同名函式,或者在不同名字空間中的同名函式,或者在同一名字空間或類中的同名過載函式,在最終的編譯和鏈結過程中是怎麼將它們區分開來的呢?為了支援c++這些特性,人們發明了所謂的符號改編(name mangling)機制。

其原理其實很簡單,就是按照函式所在名字空間、類以及引數的不同,按照一定規則對函式進行重新命名。不同的編譯器其命名規則都不盡相同,這裡我們主要介紹gnu c++編譯器所使用的規則。主要分為以下幾種情況:

1)全域性變數:

即在命名空間和類之外的變數,改編後的符號名就是變數名,也就是不做任何修改。

2)全域性函式:

以「_z」開頭,然後是函式名字元的個數,接著是函式名,最後是函式引數的別名。

關於函式引數的別名,後面還會有詳細的介紹。

3)類或命名空間中的變數或函式:

以「_zn」開頭,然後是變數或函式所在名字空間或類名字的字元長度,然後接著的是真正的名字空間或類名,然後是變數或函式名的長度和變數或函式名,後面緊跟字母「e」,最後如果是函式的話則跟引數別名,如果是變數則什麼都不用加。

4)建構函式和析構函式

以」_zn」開頭,然後是建構函式所在名字空間和類名字的字元長度,然後接著的是真正的名字空間或類名,然後建構函式接「c1」或者「c2」,析構函式接「d1」或者「d2」,然後加上字母「e」,最後接函式引數別名結束。

介紹完命名規則,下面我們再具體介紹一下函式引數別名的規則。主要分為下面幾種情況:

1)函式引數是基本型別時

每個基本型別的別名如下表:

引數型別

引數別名

void

vwchar_t

wbool

bchar

csigned char

aunsigned char

hshort

sunsigned short

tint

iunsigned int

jlong

lunsigned long

mlong long或__int64

xunsigned long long或unsigned __int64

y__int128

nunsigned __int128

ofloat

fdouble

dlong double或__float80

e__float128

g2)函式引數是類或結構體時

當函式的引數中含有類或結構體時,在類或者結構體名字前加上類或結構體名的字元長度。

例如,全域性函式int structure_func(int i, struct test  s, double d),其經過符號改編後,函式名變成了_z14structure_funci4testd。

3)函式引數是指標(*)時

當函式引數中含有指標時,該引數的別名是「p」(大寫)加上該指標指向的引數型別的別名。當引數為指標的指標時,該引數的別名是「pp」加上所指向的引數型別的別名,以此類推。

4)函式引數是一維陣列時

當函式引數中含有一維陣列時,和引數是指標的處理方式一樣,也是「p」加上作為引數的陣列其元素型別的別名。

5)函式引數是多維陣列時

對於多維陣列,第一維可以看做是指標,其它維則看做是陣列。

當函式引數中含有多維陣列時,以「p」(代表陣列的第一維)開始,後面接「a」加上各維陣列的長度,以「_」間隔,最後以下劃線加陣列元素型別的別名結束。

例如,全域性函式void multi_array_func(int a[10][10][20][30]),其經過符號改編後,函式名變成了_z16multi_array_funcpa10_a20_a30_i。

6)函式引數含有const修飾符時

當函式引數中含有const修飾符時,以「k」(大寫)開始,後面接修飾引數型別的別名。

7)函式引數是引用(&)時

當函式引數中含有引用時,該引數的別名是「r」(大寫)加上該引用所引用的變數型別的別名。

例如,全域性函式void ref_const_func(const int &i),其經過符號改編後,函式名變成了_z14ref_const_funcrki。

8)函式引數是別的命名空間中的類或結構體

當函式的引數含有別的命名空間中的類或結構體時,該引數的別名是「n」(大寫),加上空間名的長度,再加上空間名,接著是類或結構名的長度和類或結構的名字,最後以「e」(大寫)結束。

再舉乙個複雜點的例子,假設**如下:

namespace ns1;}

namespace ns2

};}

那麼myfunction經過符號改變後變成了什麼呢?答案是:_zn3ns25test210myfunctionen3ns15test1e。

最後,稍微總結一下。其實所謂gnu c++的符號改編機制非常簡單,只要記住下面幾點就可以了:

1)除了全域性變數不用做改編之外,其它所需要改編符號的時候,都是以_z開始;

2)若想表示某個符號是在命名空間或類中的,要以「n」開始,以「e」結束;

3)所有的名字空間名、類名、函式名或變數名,改編的時候都是名字所包含的字元數加上真正的名字;

4)所有的名字按照從外層到裡層的順序進行改編;

5)如果是函式的話,所有的引數按照前後出現的順序進行改編。

最後再提一句,這裡的符號改編機制都是暗地裡編譯器幫你做的。只要你的程式使用gnu c++編譯器進行編譯,它都會用上文所述的規則對你的各種符號名進行改編(包括變數和函式)。

如果你的程式有一些用c語言編寫及編譯,而另外一些用c++語言編寫及編譯,並且這兩部分還會互相呼叫到,則需要進行特殊處理。

c++程式在編譯的時候會用符號改編,而c程式在呼叫的時候並不會用符號改編,而是還用原始的函式名作為符號名進行呼叫,這樣c程式就找不到那個對應的c++函式了。

或者,倒過來,c程式在編譯的時候不會進行符號重編,而c++程式在呼叫的時候也會將這個函式名進行重編,這樣c++程式同樣也找不到那個對應的c函式了。

解決的方法是把那些需要讓c程式用到的c++程式中的變數和函式,或者c++程式用到的c程式中的變數和函式,單獨抽出來,讓編譯器不對它們進行符號重編。

具體方法是將它們用extern "c"包起來:

extern "c"

當然,也可以乙個乙個指定:

extern "c" void func();

......

C 符號和 符號的用法介紹

1.忽略轉義字元 string str c windows system32 string str c windows system32 2.字串跨行 string str line one line two line three line fore string str line one line...

反射機制的介紹

反射機制 程式在執行的過程中載入 一些 只知道相關名字 的類,以下 在程式執行時載入user類。class c class forname com.bjsxt.reflect.user 乙個類被載入後,jvm會建立乙個對應類的class物件,類的整個結構資訊會被放到class物件中。這個class物...

按位取反符號的介紹

前言 是我最近做面試題的時候遇到的符號,我不知道是什麼含義,上網上查詢之後才知道,下面我通過例題來說明這個符號使用之後的結果是什麼 例題 public class test what is the decimal value fo j at line5?a 0b 1 c 14 d 15 e an e...