每日一問3: C++中extern關鍵字的作用


extern是什么及其作用

  extern是c++引入的一個關鍵字,它可以應用於一個全局變量,函數或模板聲明,說明該符號具有外部鏈接(external linkage)屬性。也就是說,這個符號在別處定義。一般而言,C++全局變量的作用范圍僅限於當前的文件,但同時C++也支持分離式編譯,允許將程序分割為若干個文件被獨立編譯。於是就需要在文件間共享數據,這里extern就發揮了作用。

 

先導知識

符號的定義和聲明

  在介紹extern之前,我們需要了解一下變量的聲明和定義。變量的聲明指向程序表名變量的類型和名字,即使得名字為程序所知,一個文件如果想使用別處定義的名字則必須包含對那個名字的聲明。而變量的定義指申請存儲空間,並將其與變量名相關聯,除此之外,還可以為變量指定初始值。在程序中變量可以聲明多次,但只能定義一次。一般而言,定義就是聲明。但C++中由於extern的緣故,變量的聲明和定義是可以分開的。凡是沒有帶extern的聲明同時也都是定義。而對函數而言,帶有{}是定義,否則是聲明。如果想聲明一個變量而非定義它,就在變量名前添加關鍵字extern,且不要顯式的初始化變量。看下面的例子:

//fileA.cpp
int i;                //聲明並定義i
int j = 1;            //聲明並定義j
double max(double d1,double d2){} //定義

//fileB.cpp
extern int i;          //聲明i而非定義
extern int j = 2;     //定義j而非聲明,會報錯,多重定義
int j;                //錯誤,重定義,注意這里的j是在全局范圍內聲明
extern double max(double d1,double d2); //聲明

C++中的鏈接屬性

   鏈接屬性一定程度范圍決定着符號的作用域,C++中鏈接屬性有三種:none(無)、external(外部)和 internal(內部)

  • external,外部鏈接屬性。非常量全局變量和自由函數(除成員函數以外的函數)均默認為外部鏈接的,它們具有全局可見性,在全局范圍不允許重名,詳情可見例子。
  • internal,內部鏈接屬性。具有該屬性的類型有,const對象,constexpr對象,命令空間內的靜態對象(static objects in namespace scope)
  • none,在類中、函數體和代碼塊中聲明的變量默認是具有none鏈接屬性。它和internal一樣只在當前作用域可見。

extern的用法

  extern有3種用法,分別如下:

非常量全局變量的外部鏈接

   最常見的用法,當鏈接器在一個全局變量聲明前看到extern關鍵字,它會嘗試在其他文件中尋找這個變量的定義。這里強調全局且非常量的原因是,全局非常量的變量默認是外部鏈接的。

//fileA.cpp
int i = 1;         //聲明並定義全局變量i

//fileB.cpp
extern int i;    //聲明i,鏈接全局變量

//fileC.cpp
extern int i = 2;        //錯誤,多重定義
int i;                    //錯誤,這是一個定義,導致多重定義
main()
{
    extern int i;        //正確
    int i = 5;            //正確,新的局部變量i;
}

常量全局變量的外部鏈接

  常量全局變量默認是內部鏈接的,所以想要在文件間傳遞常量全局變量需要在定義時指明extern,如下所示:

//fileA.cpp
extern const int i = 1;        //定義

//fileB.cpp                    //聲明
extern const int i;

extern "C" 和extern "C++"函數聲明

   在C++中,當與字符串連用時,extern指明當前聲明使用了其他語言的鏈接規范,如extern "C",就指明使用C語言的鏈接規范。原因是,C++語言在編譯的時候為了解決函數的多態問題,會將函數名和參數聯合起來生成一個中間的函數名稱,而C語言則不會,因此會造成鏈接時無法找到對應函數的情況,此時C函數就需要用extern “C”進行鏈接指定,這告訴編譯器,請保持我的名稱,不要給我生成用於鏈接的中間函數名。C和C++對函數的處理方式是不同的.extern "C"是使C++能夠調用C寫作的庫文件的一個手段,如果要對編譯器提示使用C的方式來處理函數的話,那么就要使用extern "C"來說明。

  例子如下:

// 聲明printf函數使用C鏈接
extern "C" int printf(const char *fmt, ...);


//聲明指定的頭文件內所有的東西都使用 C 鏈接
extern "C" {
#include <stdio.h>
}

//  聲明函數ShowChar和GetChar使用 C 鏈接
extern "C" {
    char ShowChar(char ch);
    char GetChar(void);
}

//  定義函數 ShowChar 和 GetChar 使用 C 鏈接
extern "C" char ShowChar(char ch) {
    putchar(ch);
    return ch;
}

extern "C" char GetChar(void) {
    char ch;
    ch = getchar();
    return ch;
}

// 聲明全局變量 errno 為C鏈接
extern "C" int errno;

//又比如,在程序中常見的代碼段
#ifdef __cplusplus  
extern "C" {  
#endif  
  
/**** some declaration or so *****/  
  
#ifdef __cplusplus  
}  
#endif

//這里__cplusplus是cpp中的自定義宏,定義了這個宏就表明這是一段cpp的代碼,也就是說,
//上面的代碼的含義是:如果這是一段cpp的代碼,那么加入extern "C"{和}處理其中的代碼。

 

一些問題

  •  使用extern和包含頭文件來引用函數有什么區別呢?

  與include相比,extern引用另一個文件的范圍小,include可以引用另一個文件的全部內容。extern的引用方式比包含頭文件要更簡潔。extern的使用方法是直接了當的,想引用哪個函數就用extern聲明哪個函數。這樣做的一個明顯的好處是,會加速程序的編譯(確切的說是預處理)的過程,節省時間。在大型C程序編譯過程中,這種差異是非常明顯的。

注意事項

  • 不要把變量定義放入.h文件,這樣容易導致重復定義錯誤
  • 盡量使用static關鍵字把變量定義限制於該源文件作用域,除非變量被設計成全局的。
  • 可以在頭文件中聲明一個變量,在用的時候包含這個頭文件就聲明了這個變量。

 

參考博客:

1.https://blog.csdn.net/duangyhn/article/details/81393810

1.https://docs.microsoft.com/en-us/cpp/cpp/extern-cpp?view=vs-2019

 


免責聲明!

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



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