簡體   English   中英

外部“C”聲明如何工作?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM