淺談 extern "C"


  今天上課實在無聊,就看了看 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;
}

 

  以上就是今天的全部收獲了,分享給大家。

 


免責聲明!

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



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