日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

extern “C”

發(fā)布時(shí)間:2023/12/20 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 extern “C” 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在C++ 程序中調(diào)用被C 編譯器編譯后的函數(shù),為什么要加extern “C”?

這個(gè)問(wèn)題包括兩部分。一部分是extern的作用,一個(gè)是“C”的作用。

下面將從這兩個(gè)部分分別整理下相關(guān)知識(shí)。

一、extern

1、?聲明外部變量

?現(xiàn)代編譯器一般采用按文件編譯的方式,因此在編譯時(shí),各個(gè)文件中定義的全局變量是互相透明的,也就是說(shuō),在編譯時(shí),全局變量的可見(jiàn)域限制在文件內(nèi)部。

我們?cè)贏。cpp文件中定義如下句子

int i=0;

在B.cpp文件中定義如下句子

int i=0;

然后分別編譯兩個(gè)文件,此時(shí),編譯成功。而當(dāng)我們將兩個(gè)文件進(jìn)行鏈接時(shí),卻提示錯(cuò)誤。錯(cuò)誤提示是error C2086: 'int i' : redefinition。

這就是說(shuō),在編譯階段,各個(gè)文件中定義的全局變量相互是透明的,編譯A時(shí)覺(jué)察不到B中也定義了i,同樣,編譯B時(shí)覺(jué)察不到A中也定義了i

但是到了鏈接階段,要將各個(gè)文件的內(nèi)容“合為一體”,因此,如果某些文件中定義的全局變量名相同的話,在這個(gè)時(shí)候就會(huì)出現(xiàn)錯(cuò)誤,也就是上面提示的重復(fù)定義的錯(cuò)誤。

因此,各個(gè)文件中定義的全局變量名不可相同。

在鏈接階段,各個(gè)文件的內(nèi)容(實(shí)際是編譯產(chǎn)生的obj文件)是被合并到一起的,因而,定義于某文件內(nèi)的全局變量,在鏈接完成后,它的可見(jiàn)范圍被擴(kuò)大到了整個(gè)程序。

???????這樣一來(lái),按道理說(shuō),一個(gè)文件中定義的全局變量,可以在整個(gè)程序的任何地方被使用,舉例說(shuō),如果A文件中定義了某全局變量,那么B文件中應(yīng)可以用該變量。修改我們的程序,加以驗(yàn)證:

將B.cpp的代碼改為

i = 100;

此時(shí),編譯出錯(cuò),提示i未定義。

?

其實(shí)出現(xiàn)這個(gè)錯(cuò)誤是意料之中的,因?yàn)?#xff1a;文件中定義的全局變量的可見(jiàn)性擴(kuò)展到整個(gè)程序是在鏈接完成之后,而在編譯階段,他們的可見(jiàn)性仍局限于各自的文件。

?

編譯器的目光不夠長(zhǎng)遠(yuǎn),編譯器沒(méi)有能夠意識(shí)到,某個(gè)變量符號(hào)雖然不是本文件定義的,但是它可能是在其它的文件中定義的。

?

???????雖然編譯器不夠遠(yuǎn)見(jiàn),但是我們可以給它提示,幫助它來(lái)解決上面出現(xiàn)的問(wèn)題。這就是extern的作用了。

?extern的原理很簡(jiǎn)單,就是告訴編譯器:“你現(xiàn)在編譯的文件中,有一個(gè)標(biāo)識(shí)符雖然沒(méi)有在本文件中定義,但是它是在別的文件中定義的全局變量,你要放行!”

于是,我們?cè)贐.cpp文件中,將代碼改為

extern int i;

此時(shí)重新編譯,ok,沒(méi)問(wèn)題了。

因此我們可以總結(jié)如下:extern是C/C++語(yǔ)言中表明函數(shù)和全局變量作用范圍(可見(jiàn)性)的關(guān)鍵字,該關(guān)鍵字告訴編譯器,其聲明的函數(shù)和變量可以在本模塊或其它模塊中使用

通常,在模塊的頭文件中對(duì)本模塊提供給其它模塊引用的函數(shù)和全局變量以關(guān)鍵字extern聲明。

二、“C”

2.1 C方式編譯和C++方式編譯

相對(duì)于CC++中新增了諸如重載等新特性,對(duì)于他們的編譯,必然有一些重要的區(qū)別。根據(jù)我們實(shí)驗(yàn),以及查看的資料,可以很容易就得知這些區(qū)別是什么。那就是:

?

作為一種面向?qū)ο蟮恼Z(yǔ)言,C++支持函數(shù)重載,而過(guò)程式語(yǔ)言C則不支持。函數(shù)被C++編譯后在符號(hào)庫(kù)中的名字與C語(yǔ)言的不同。例如,假設(shè)某個(gè)函數(shù)的原型為:

?

void foo( int x, int y );
  

?

該函數(shù)被C編譯器編譯后在符號(hào)庫(kù)中的名字為_(kāi)foo,而C++編譯器則會(huì)產(chǎn)生像_foo_int_int之類(lèi)的名字(不同的編譯器可能生成的名字不同,但是都采用了相同的機(jī)制,生成的新名字稱為“mangled name”)。

_foo_int_int這樣的名字包含了函數(shù)名、函數(shù)參數(shù)數(shù)量及類(lèi)型信息,C++就是靠這種機(jī)制來(lái)實(shí)現(xiàn)函數(shù)重載的。例如,在C++中,函數(shù)void foo( int x, int y )與void foo( int x, float y )編譯生成的符號(hào)是不相同的,后者為_(kāi)foo_int_float。

?

同樣地,C++中的變量除支持局部變量外,還支持類(lèi)成員變量和全局變量。用戶所編寫(xiě)程序的類(lèi)成員變量可能與全局變量同名,我們以"."來(lái)區(qū)分。而本質(zhì)上,編譯器在進(jìn)行編譯時(shí),與函數(shù)的處理相似,也為類(lèi)中的變量取了一個(gè)獨(dú)一無(wú)二的名字,這個(gè)名字與用戶程序中同名的全局變量名字不同。

因此,我們在文件B.CPP中定義函數(shù)void func()main函數(shù)位于文件A.CPP,在main函數(shù)中調(diào)用了B中定義的函數(shù)func()

?

???????要在A中調(diào)用B中定義的函數(shù),必須要加上該函數(shù)的聲明。如本例中的void func();就是對(duì)函數(shù)func()的聲明。

?

如果沒(méi)有聲明的話,編譯A.CPP時(shí)就會(huì)出錯(cuò)。因?yàn)榫幾g器的目光只局限于被編譯文件,必須通過(guò)加入函數(shù)聲明來(lái)告訴編譯器:“某個(gè)函數(shù)是定義在其它的文件中的,你要放行!”,這一點(diǎn)跟用extern來(lái)聲明外部全局變量是一個(gè)道理。

B.h內(nèi)容為:

int func();

B.cpp內(nèi)容為:

int func(int i){return 0;}

A.cpp

#include "B.h"

void main()

{ }

此時(shí),以C編譯方式編譯如上代碼,其結(jié)果為:

?

PUBLIC????_func

PUBLIC????_main

然后,以C++方式編譯,編譯結(jié)果為:

?

PUBLIC?????func@@YAHH@Z?????????????????????????????????????????; func

?

PUBLIC????_main

我們可以發(fā)現(xiàn)這兩種編譯方式的不同之處了。

2.2?不同編譯方式下的函數(shù)調(diào)用

?如果在工程中,不僅有CPP文件,還有以C方式編譯的C文件,函數(shù)調(diào)用就會(huì)有一些微妙之處。我們將B.CPP改作B.C

對(duì)A.CPPB.C分別編譯,都沒(méi)有問(wèn)題,但是鏈接時(shí)出現(xiàn)錯(cuò)誤。

提示:

Linking...

?

A.obj : error LNK2001: unresolved external symbol "void __cdecl func(void)" (?func@@YAXXZ)

?

Debug/A.exe : fatal error LNK1120: 1 unresolved externals

?

Error executing link.exe.

?

A.exe - 2 error(s), 0 warning(s)

?

原因就在于不同的編譯方式產(chǎn)生的沖突。

?對(duì)于文件A,是按照C++的方式進(jìn)行編譯的,其中的func()調(diào)用被編譯成了?

PUBLIC?????func@@YAHH@Z?????????????????????????????????????????; func

?

如果B文件也是按照C++方式編譯的,那么B中的func函數(shù)名也會(huì)被編譯器改成?func1@@YAXXZ,這樣的話,就沒(méi)有任何問(wèn)題。

???????但是現(xiàn)在對(duì)B文件,是按照C方式編譯的,B中的func函數(shù)名被改成了_func,這樣一來(lái),A中的call ?func1@@YAXXZ這個(gè)函數(shù)調(diào)用就沒(méi)有了著落,因?yàn)樵阪溄悠骺磥?lái),B文件中沒(méi)有名為?func1@@YAXXZ的函數(shù)。

???????事實(shí)是,我們編程者知道,B文件中有A中調(diào)用的func函數(shù)的定義,只不過(guò)它是按照C方式編譯的,故它的名字被改成了_func。因而,我們需要通過(guò)某種方式告訴編譯器:“B中定義的函數(shù)func()經(jīng)編譯后命名成了_func,而不是?func1@@YAXXZ,你必須通過(guò)call _func來(lái)調(diào)用它,而不是call ?func1@@YAXXZ。”簡(jiǎn)單的說(shuō),就是告訴編譯器,調(diào)用的func()函數(shù)是以C方式編譯的,fun();語(yǔ)句必須被編譯成call _func;而不是call ?func1@@YAXXZ

? ? ??我們可以通過(guò)extern關(guān)鍵字,來(lái)幫助編譯器解決上面提到的問(wèn)題。

?對(duì)于本例,只需將A.CPP改成如下即可:

?

//A.CPP

?

extern "C"{?void func();}

?

void main(){?func();}

察看匯編代碼,發(fā)現(xiàn)此時(shí)的func();語(yǔ)句被編譯成了call _func

三、C中調(diào)用C++代碼

在C中引用C++語(yǔ)言中的函數(shù)和變量時(shí),C++的頭文件需添加extern "C",但是在C語(yǔ)言中不能直接引用聲明了extern "C"的該頭文件,應(yīng)該僅將C文件中將C++中定義的extern "C"函數(shù)聲明為extern類(lèi)型。   筆者編寫(xiě)的C引用C++函數(shù)例子工程中包含的三個(gè)文件的源代碼如下:   //C++頭文件 cppExample.h   #ifndef CPP_EXAMPLE_H   #define CPP_EXAMPLE_H   extern "C" int add( int x, int y );   #endif   //C++實(shí)現(xiàn)文件 cppExample.cpp   #include "cppExample.h"   int add( int x, int y )   {   return x + y;   }   /* C實(shí)現(xiàn)文件 cFile.c   /* 這樣會(huì)編譯出錯(cuò):#include "cppExample.h" */   extern int add( int x, int y );   int main( int argc, char* argv[] )   {   add( 2, 3 );   return 0;   }   總結(jié) C和C++對(duì)函數(shù)的處理方式是不同的.extern "C"是使C能夠調(diào)用C++寫(xiě)的庫(kù)文件的一個(gè)手段,如果要對(duì)編譯器提示使用C的方式來(lái)處理函數(shù)的話,那么就要使用extern "C"來(lái)說(shuō)明。

?

?

總結(jié)

以上是生活随笔為你收集整理的extern “C”的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。