extern "C" 與函數重載


前言

如果向要在一個文件中使用另一個文件中的變量,不能在頭文件中定義全局變量,因為被多個文件包含后會導致編譯出錯,並且靜態的static變量,只能在本文件內使用,這時候就可以使用extern關鍵字。

extern 關鍵字:

  首先還是先看一下 extern 關鍵字的作用:extern關鍵字可以置於變量或函數前,以標示變量或函數的定義在別的文件中,提示編譯器遇到此變量或函數時在其他模塊中尋找其定義。

  通常情況下,比如我們在頭文件  "b.h"  中聲明了一個函數,然后在 "b.cpp" 中實現了該函數,當在 "main.cpp" 中調用 "b.h" 中聲明的函數時,只需要在 ""main.cpp" 中 #include "b.h" 就可以了。例子如下:

 1 //b.h
 2 #ifndef _B_
 3 #define _B_
 4 
 5 #include<iostream>
 6 using namespace std;
 7 
 8 void test();
 9 
10 #endif // _B_
1 #include "b.h"
2 void test()
3 {
4     cout << "test" << endl;
5 }
 1 #include<iostream>
 2 #include"b.h"
 3 
 4 using namespace std;
 5 
 6 int main()
 7 {
 8     test();
 9     system("pause");
10     return 0;
11 }

除了通過 #include "b.h" 這樣的方式能調用到 "b.h" 中的函數外,還有下面這種方式也能調用到"b.h" 的函數。

 1 #include<iostream>
 2 //#include"b.h"  //在這里,不注釋掉也是可以得,但是在對於 變量 來說時就必須注釋掉了
 3 
 4 using namespace std;
 5 
 6 extern void test();    //告訴編譯器test()函數聲明在其他文件中
 7 
 8 int main()
 9 {
10     test();
11     system("pause");
12     return 0;
13 }

上面是對於函數而言,那么要是在 "b.h" 中定義了一個全局變量 int x,(記住是全局變量哦!)現在我們想在 "main.cpp" 中訪問變量 x, 那該怎么辦呢? 會不會 #include "b.h" 后就可以直接訪問了呢?我們先試一下:

 1 //b.h
 2 #ifndef _B_
 3 #define _B_
 4 
 5 #include<iostream>
 6 using namespace std;
 7 
 8 int x = 10;
 9 
10 void test();
11 
12 #endif // _B_
1 //main.cpp
2 #include "b.h"
3 
4 int main()
5 {
6     cout << "x=" << x << endl;
7     system("pause");
8     return 0;
9 }

我們在 "main.cpp" 中輸出變量 x, 在vs2017中會報錯。  (上一個增量鏈接沒有生成它;正在執行完全鏈接,error LNK2005: "int x" (?x@@3HA) 已經在 b.obj 中定義)

通過 #include "b.h" 這種方式就想訪問到變量 x 太天真了,是訪問不到的,因為 "b.h" 的全局變量 x 的訪問作用域是文件作用域,它只能在 "b.h" 這個文件中進訪問,記住是只能在 "b.h" 中進行訪問,在 "b.cpp"中通過 #include "b.h" 你也是不能訪問的。 那么現在我們有沒有其他方式能在 "main.cpp" 中訪問到變量 x 呢?當然有,通過 "extern" 關鍵字能達到目的。用法如下:

 1 //main.cpp
 2 //#include "b.h"  //一定要注釋掉
 3 #include <iostream>
 4 using namespace std;
 5 extern int x;
 6 
 7 int main()
 8 {
 9     cout << "x=" << x << endl;
10     system("pause");
11     return 0;
12 }

說明:如果在一個文件中使用extren引入外部變量,在這個文件中修改這個變量,就等於修改了該外部變量。

說了那么多廢話,終於把 extern 關鍵字說清楚了。接下來這個才是extern "C" 。

extern "C" 

  要說清楚 extern "C" 是怎么一回事,還得先說說C++函數重載的那些事。C++有函數重載,而C語言是沒有函數重載的。函數重載是指兩個或多個函數之間函數名相同,參數列表不同,參數列表不同可以是參數的個數不同,或者是參數的個數相同但參數類型不同,需要注意的是如果函數名相同,參數列表完全相同但返回值類型不同是不能構成函數重載的。C++有函數重載是因為當生成obj中間文件/目標文件的時候,C++編譯器把原函數名與參數信息結合,產生了一個獨特的內部名字,比如有兩個函數 void foo(int x) 和 void foo(void) ,最終產生的內部名字就是 _foo_int 和 _foo_void (實際產生的內部名字的命名規則應該不是這樣的,這里我們並不關心它的命名規則是怎樣的,只需要體會這個意思就可以了),通過這樣編譯器就能區分 void foo(int x) 和 void foo(void)這兩個函數了。但是在C語言里面,並不會產生這樣的內部名字,如果C語言里面有兩個函數 void foo(int x) 和void foo(void),那么當生成obj中間文件/目標文件的時候,產生的名字就是 _foo 和 _foo 這樣兩個名字相同,C編譯器就不能將兩個函數區分開了,所以C語言里面也就沒了函數重載。

  正是由於C++編譯器 和 C編譯器對函數名處理方式的不同,當我們的 C++ 程序調用 C 程序或者 C 程序調用 C++程序時就會存在問題。 有了問題那當然就得解決,於是就有了 extern "C" 的出現。

  所以說到底 extern "C" 的作用是用來解決名字匹配問題,實現 C 與 C++ 的混合編程。擺這么一句話在這里顯得很蒼白無力,還是舉例說明一下。

 C++中函數重載,編譯器的命名規則是

 1 int fun(int a, int b)
 2 {
 3     return a + b;
 4 }
 5 char fun(char a, char b)
 6 {
 7     return a + b;
 8 }
 9 
10 void main()
11 {
12     cout<<fun('A','B')<<endl;
13     cout<<fun(10, 20)<<endl;
14 }

 上述C++的兩個重載函數被編譯器命名為下:

 

在不同的平台的編譯器中,命名規則不一定相同,其中的DDD和HHH代表返回值、第一個參數類型、第二個參數類型
如果再加一個參數
1 int fun(int a, int b, char c)
2 {
3     return a + b;
4 }

 命名會變為

 

可以得出,C++編譯器的函數命名規則

而在C語言中,編譯器將函數命名為
所以不論多少個函數,編譯器都會命名為一樣的函數,即使你的參數不一樣,編譯器也無法調動這個函數,所以就會報錯
在C++中加extern “C” 
 1 extern "C" int fun(int a, int b)
 2 {
 3     return a + b;
 4 }
 5 extern "C" char fun(char a, char b)
 6 {
 7     return a + b;
 8 }
 9 
10 void main()
11 {
12     cout<<fun('A','B')<<endl;
13     cout<<fun(10, 20)<<endl;
14 }

上述C++程序中,加一個extern "C"沒問題,加兩個就會報錯了,加了extern "C"就等於用C語言的方式編譯,函數的命名規則會變成C語言的,然而C語言不允許函數的重載。

在C++程序中調用被C編譯器編譯后的函數,加extern“C“聲明,為了預防函數的重載出現。

函數重載的另一個問題

為什么返回值不能確定函數重載,如果有以下程序
1 int fun(int a, int b)
2 {
3     return a + b;
4 }
5 char fun(int a, int b)
6 {
7     return a + b;
8 }

 如果按照C++編譯器的命名,這兩個函數的命名也是不同的,為什么不能重載呢?

 

這時候並不是不行,而是調用一個函數,可以調用一個,也可以調用另一個,然后編譯器並不知道要調到哪個,出現了二義性,而不同的函數參數,在編譯時就決定了調用哪個,編譯器也不知道要將返回值給哪個類型,所以僅靠返回值是不能用來重載函數的。

 

[參考:https://www.cnblogs.com/418ks/p/6837669.html]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM