在c/c++中, 乙個程式要執行起來, 需要經歷以下幾個階段: 預處理, 編譯, 彙編, 鏈結.
而名字修飾是一種在編譯過程中, 將函式, 變數的名稱重新改變的機制, 簡單來說就是編譯器為了區分各個函式, 將函式通過某種演算法, 重新修飾為乙個全域性唯一的名稱.
在c語言當中的名字修飾規則非常簡單, 只是在函式名字前面新增了下劃線. 舉個例子來說,看下面的**
#include #include int add(int left, int right);
int main()
編譯器報錯:
上述的add函式只給了宣告沒有給定義, 因此在最後鏈結的時候就會報錯從報錯的結果中可以看到, c語言只是簡單的在函式名前新增下劃線. 因此當工程中存在相同函式名的函式時,就會產生衝突.
但c++中不同於c語言, 由於c++要支援函式過載, 命名空間等, 從而導致其修飾規則也會變得比較複雜, 在不同的編譯器中, 在底層的實現方式也可能都有差異. 舉例來看
#include using namespace std;
int add(int left, int right);
double add(double left, double right);
int main()
編譯器報錯:
通過上述報錯可以看出, 編譯器實際在底層使用的不是add名字, 而是被重新修飾過的乙個比較複雜的名字, 被重新修飾後的名字中包含了: 函式的名字以及引數型別. 這也就是為什麼函式過載中幾個同名函式要求其引數列表不同的原因, 只要引數列表不同, 編譯器在編譯時通過對函式名字進行重新修飾, 將引數型別包含在最終的名字中, 就可以保證名字在底層的全域性唯一性.
常見的一些底層修飾
int func(int) 修飾後 ?func@@yahh@z
float func(float) 修飾後 ?func@@yamm@z
int c::func(int) 修飾後
?func@c@@aaehh@z
int c::c2::func(int) 修飾後
?func@c2@c@@aaehh@z
int n::func(int) 修飾後
?func@n@@yahh@z
int n::c::func(int) 修飾後
?func@c@n@@aaehh@z
總結來說, 在c++中, 名字修飾是由"?函式名@網域名稱1@網域名稱2…@@引數列表@z"的格式構成的, 包含
a. 函式名
b. 所在域
c. 引數列表
也正因為如此, 在c++中, 以上三個必須完全相同, 才會出現衝突, 這也就是函式過載的原理.
關於 extern 「c」
有時候在c++工程中我們可能會需要某些函式按照c的風格來編譯, 在函式前加extern 「c」, 意思就是告訴編譯器, 將函式按照c語言規則來進行編譯
比如, 建立乙個c++工程, **如下
#include using namespace std;
int func(int a)
int func(int a, int b)
int main()
這樣直接執行不會產生錯誤, 接下來我們在此基礎上進行修改
#include using namespace std;
extern "c"
int func(int a, int b) }
int main()
這樣編譯器就會報錯
關於C 中的static修飾符
修飾類的時候表示可以不用例項化而直接使用的類。比如 class a public void fun2 使用fun1的話就直接a.fun1 就可以了,如果實用fun2的話必須例項乙個物件出來才行變數宣告成static的話當整個程式退出時才釋放空間 比如你申請了乙個變數名字交 static int p ...
C 中的名字空間
摘抄自 c primer plus 在c 中,名稱可以是變數 函式 結構 列舉 類以及類和結構的成員。當隨著專案的增大,名稱相互衝突的可能性也將增加。使用多個廠商的類庫時,可能導致名稱衝突。例如,兩個庫可能都定義了名為list tree和node的類,但定義的方式不相容。使用者可能希望使用乙個庫的l...
關於C 中 map 的意義以及用法
map,顧名思義就是地圖。其實就是key,value的對應。當你需要快速的獲取對應key的value的時候,就可以使用map了。例如乙個人是有名字,但是這個人還有其他的屬性,例如年齡,性別等等。這個人就會被封裝為乙個物件。如果有很多個人,我們需要快速的根據乙個人的名字獲取對應名字的物件,這個時候ma...