學習過C++的人都知道,extern關鍵字可以置于變量或者函數前,以標示變量或者函數的定義在別的文件中,提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。這里起到的是聲明作用范圍的用處。另外,extern還可以與”C”連用,作為鏈接指示。本文就此進行實例說明如下:
一、C++名字修飾(Name Mangling)
首先需要從C++的重載說起,在C++中函數重載指的是幾個函數的函數名相同,參數列表不同。那么當生成obj中間文件/目標文件的時候,C++編譯器如何區分這幾個重載函數呢?可以通過把原函數名與參數信息結合,產生一個獨特的內部名字,這種技術叫做名字修飾(Name Mangling)。名字修飾規則沒有一個標準,所以不同的編譯器的名字修飾規則也不一樣。
下面是一組函數,其中f()函數重載了:
int f (void) { return 1; } int f (int) { return 0; } void g (void) { int i = f(), j = f(0); }
f(void)和f(int)是不同的函數,除了函數名相同以外沒有任何關系。當生成obj目標文件時,為了區分它們,C++編譯器根據參數信息進行了名字修飾:
int __f_v (void) { return 1; } int __f_i (int) { return 0; } void __g_v (void) { int i = __f_v(), j = __f_i(0); }
注意g()也被名字修飾了,雖然沒有任何名字沖突。名字修飾應用于C++的任何符號。
二、為什么要使用extern “C”?
C語言中沒有名字修飾(Name Mangling),因為C語言不支持函數重載。但是如果C++中含有C代碼,在編譯時C++編譯器對C代碼的函數也會進行名字修飾,函數名變了以后,將導致在C運行庫中找不到對應函數,發生鏈接錯誤。
// 將下面的代碼保存為.cpp文件,并用C++編譯器編譯 int printf(const char *format,...); int main() { printf("GeeksforGeeks"); return 0; }
輸出:
/tmp/ccQBO9Im.o:在函數‘main'中: test.cpp:(.text+0xf):對‘printf(char const*, ...)'未定義的引用 collect2: 錯誤:ld 返回 1
為了防止C++編譯器對C代碼進行名字修飾,我們將C代碼用extern “C”進行鏈接指定,告訴編譯器,在生成中間文件時,不要對這部分代碼進行名字修飾,而是生成符合C規則的中間符號名。
extern "C" { int printf(const char *format,...); } int main() { printf("Hello!"); return 0; }
添加了extern “C”鏈接指示后,上面的代碼就能夠正常運行了。
附:所有的C風格的頭文件(stdio.h, string.h, … 等等)都有在extern “C”下聲明,形式如下:
#ifdef __cplusplus extern "C" { #endif /* Declarations of this file */ #ifdef __cplusplus } #endif
新聞熱點
疑難解答
圖片精選