[英]How does an extern “C” declaration work?
我正在學習編程語言課程,我們正在談論extern "C"
聲明。
除了“它接口C和C ++”之外,這個聲明如何在更深層次上工作? 這又如何影響程序中發生的綁定?
extern "C"
用於確保后面的符號不被修飾(修飾)。
例:
假設我們在名為test.cpp
的文件中有以下代碼:
extern "C" {
int foo() {
return 1;
}
}
int bar() {
return 1;
}
如果你運行gcc -c test.cpp -o test.o
看看符號名稱:
00000010 T _Z3barv
00000000 T foo
foo()
保留其名稱。
讓我們看一下可以在C和C ++中編譯的典型函數:
int Add (int a, int b)
{
return a+b;
}
現在在C中,該函數在內部稱為“_Add”。 而使用名為name-mangling的系統在內部完全不同地調用C ++函數。 它基本上是一種命名函數的方法,以便具有不同參數的相同函數具有不同的內部名稱。
因此,如果在add.c中定義了Add(),並且你在add.h中有原型,那么如果你試圖在一個C ++文件中包含add.h,你就會遇到問題。 因為C ++代碼正在尋找名稱與add.c中的名稱不同的函數,所以會出現鏈接器錯誤。 要解決該問題,您必須通過此方法包含add.c:
extern "C"
{
#include "add.h"
}
現在,C ++代碼將鏈接到_Add而不是C ++名稱的版本。
這是表達式的一個用途。 最重要的是,如果你需要在C ++程序中編譯嚴格的C代碼(通過include語句或其他方法),你需要用extern“C”{...}聲明來包裝它。
當您使用extern“C”標記代碼塊時,您告訴系統使用C樣式鏈接。
這主要影響鏈接器破壞名稱的方式。 您可以從鏈接器中獲得標准的C風格命名,而不是使用C ++樣式名稱修改(支持運算符重載更復雜)。
在C ++中,函數的名稱/符號實際上被重命名為其他類,以便不同的類/命名空間可以具有相同簽名的函數。 在C中,功能全局定義,不需要這樣的自定義重命名過程。
為了使C ++和C相互通信,“extern C”指示編譯器不要使用C約定。
應該注意, extern "C"
也修改了函數的類型。 它不僅修改較低級別的內容:
extern "C" typedef void (*function_ptr_t)();
void foo();
int main() { function_ptr_t fptr = &foo; } // error!
&foo
的類型不等於typedef指定的類型(雖然代碼被某些編譯器接受,但並非所有編譯器都接受)。
extern C通過C ++編譯器影響名稱修改。 它是一種讓C ++編譯器不會破壞名稱的方法,或者更確切地說是以與C編譯器相同的方式來破壞它們。 這是它與C和C ++接口的方式。
舉個例子:
extern "C" void foo(int i);
將允許該函數在C模塊中實現,但允許從C ++模塊調用它。
當試圖讓C模塊調用C ++模塊中定義的C ++函數(顯然C不能使用C ++類)時,就會遇到麻煩。 C編譯器不喜歡extern "C"
。
所以你需要使用這個:
#ifdef __cplusplus
extern "C" {
#endif
void foo(int i);
#ifdef __cplusplus
}
#endif
現在,當它出現在頭文件中時,C和C ++編譯器都會對聲明感到滿意,現在它可以在C或C ++模塊中定義,並且可以由C和C ++代碼調用。
extern“C”表示附帶的代碼使用C風格的鏈接和名稱修改。 C ++使用更復雜的名稱修改格式。 這是一個例子:
http://en.wikipedia.org/wiki/Name_mangling
int example(int alpha, char beta);
在C: _example
在C ++中: __Z7exampleic
更新:正如GManNickG在評論中指出的那樣,名稱修改的模式依賴於編譯器。
extern“C”,是一個用C綁定聲明函數的關鍵字,因為C編譯器和C ++編譯器會將源代碼轉換為目標文件中的不同形式:
例如,代碼段如下:
int _cdecl func1(void) {return 0}
int _stdcall func2(int) {return 0}
int _fastcall func3(void) {return 1}
32位C編譯器將翻譯表單中的代碼,如下所示:
_func1
_func2@4
@func3@4
在cdecl中,func1將翻譯為' _name '
在stdcall中,func2將翻譯為' _name @ X '
在fastcall中,func2將翻譯為' @ name @ X '
' X '表示參數列表中參數的字節數。
Windows上的64位約定沒有前導下划線
在C ++中,引入了類,模板,命名空間和運算符重載,因為不允許兩個具有相同名稱的函數,C ++編譯器在符號名稱中提供類型信息,
例如,代碼段如下:
int func(void) {return 1;}
int func(int) {return 0;}
int func_call(void) {int m=func(), n=func(0);}
C ++編譯器將如下轉換代碼:
int func_v(void) {return 1;}
int func_i(int) {return 0;}
int func_call(void) {int m=_func_v(), n=_func_i(0);}
'_v'和'_i'是'void'和'int'的類型信息
這是msdn的引用
“extern關鍵字聲明一個變量或函數,並指定它具有外部鏈接(其名稱可以從其定義的文件以外的文件中看到)。當修改變量時,extern指定變量具有靜態持續時間(它被分配)當程序開始並在程序結束時解除分配)。變量或函數可以在另一個源文件中定義,或者稍后在同一文件中定義。文件范圍內的變量和函數的聲明默認是外部的。
http://msdn.microsoft.com/en-us/library/0603949d%28VS.80%29.aspx
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.