今天上課實在無聊,就看了看 extern "C" 的作用,看了以后對它有了一點點理解,在這里給大家分享一下(本菜雞水平有限,如若有說得不對的地方,還望大家指出)。
extern 關鍵字:
首先還是先看一下 extern 關鍵字的作用:extern關鍵字可以置於變量或函數前,以標示變量或函數的定義在別的文件中,提示編譯器遇到此變量或函數時在其他模塊中尋找其定義。
通常情況下,比如我們在頭文件 "b.h" 中聲明了一個函數,然后在 "b.cpp" 中實現了該函數,當在 "main.cpp" 中調用 "b.h" 中聲明的函數時,只需要在 ""main.cpp" 中 #include "b.h" 就可以了。例子如下:
//b.h #pragma once #include <iostream> using namespace std; void test();
//b.cpp #include "b.h" void test() { cout << "hello world" << endl; }
//main.cpp #include "b.h" int main() { test(); system("pause"); return 0; }
除了通過 #include "b.h" 這樣的方式能調用到 "b.h" 中的函數外,還有下面這種方式也能調用到"b.h" 的函數。
//main.cpp
//#include "b.h" //在這里,不注釋掉也是可以得,但是在對於 變量 來說時就必須注釋掉了 #include <iostream> using namespace std; extern void test();//告訴編譯器test()函數聲明在其他文件中 int main() { test(); system("pause"); return 0; }
上面是對於函數而言,那么要是在 "b.h" 中定義了一個全局變量 int x,(記住是全局變量哦!)現在我們想在 "main.cpp" 中訪問變量 x, 那該怎么辦呢? 會不會 #include "b.h" 后就可以直接訪問了呢?我們先試一下:
//b.h #pragma once #include <iostream> using namespace std; int x = 10; void test();
//main.cpp #include "b.h" int main() { cout << "x=" << x << endl; system("pause"); return 0; }
我們在 "main.cpp" 中輸出變量 x, 此時編譯時沒有問題的,但是當運行時,它就會報錯:fatal error LNK1169: 找到一個或多個多重定義的符號。 通過 #include "b.h" 這種方式就想訪問到變量 x 太天真了,是訪問不到的,因為 "b.h" 的全局變量 x 的訪問作用域是文件作用域,它只能在 "b.h" 這個文件中進訪問,記住是只能在 "b.h" 中進行訪問,在 "b.cpp"中通過 #include "b.h" 你也是不能訪問的。 那么現在我們有沒有其他方式能在 "main.cpp" 中訪問到變量 x 呢?當然有,通過 "extern" 關鍵字能達到目的。用法如下:
//main.cpp //#include "b.h" //一定要注釋掉 #include <iostream> using namespace std; extern int x; int main() { cout << "x=" << x << endl; system("pause"); return 0; }
說了那么多廢話,終於把 extern 關鍵字說清楚了。(我想我是說清楚了,嘿嘿嘿), 接下來這個才是今天的狠角色。
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++ 調用 C 語言函數:
//a.h #include <stdio.h> void test(int x, double y);
//a.c #include "a.h" void test(int x, double y) { printf("hello world\n"); }
上面的test函數是 C 語言實現的,我們在 "main.cpp" 進行調用,首先通過如下方式進行調用:
//main.cpp #include "a.h" int main() { test(1,1.2); getchar(); return 0; }
通過上面這種方式調用,編譯能通過,但是運行時就報錯了:fatal error LNK1120: 1 個無法解析的外部命令說明在 main.cpp 里面的 test 並沒有被找到。解決這個問題就需要 extern "C" 出場了,如下所示:
//main.cpp extern "C"{ #include "a.h" } int main() { test(1,1.2); getchar(); return 0; }
通過以上這種方式當然就能成功運行了,extern "C" 的作用就是告訴 C++ 編譯器你不要把我的 "a.h" 里的內容當C++語法來編譯,給我原封不動的當 C 語言的語法來編譯。當然也可以換用下面的這種寫法:
//main.cpp #include <iostream> using namespace std; extern "C" void test(int x, double y); int main() { test(1,1.2); getchar(); return 0; }
C 調用 C++函數:
上面給大家展示了C++語言如何調用C函數,那么C語言又如何調用C++函數呢?
//b.h #pragma noce #include <iostream> using namespace std; #ifdef __cplusplus extern "C" { #endif void test(); #ifdef __cplusplus } #endif
//b.cpp #include "b.h" void test() { cout << "hello world" << endl; }
//main.c #include <stdio.h> extern void test(); int main() { test(); getchar(); return 0; }
以上就是今天的全部收獲了,分享給大家。