extern作用の詳細

3223 ワード

externの役割1:外部変数の宣言
現代のコンパイラでは一般的にファイルごとにコンパイルする方式が採用されているため、コンパイル時に各ファイルで定義されるグローバル変数は互いに透明であり、すなわちコンパイル時にグローバル変数の可視領域がファイル内部に制限される.例1:
A.cppとB.cppの2つの簡単なC++ファイルを含むプロジェクトを作成しました.
//A.cpp:
int iRI;
int main()
{
//.....
}

//B.cpp
int iRI;

gcc A.cpp -c
gcc B.cpp -c
   A.o, B.o     。
  gcc A.o B.o -o test ,
main.o:(.bss+0x0): multiple definition of `iRI'
b.o:(.bss+0x0): first defined here
  :   。

すなわち,コンパイル段階では,各ファイルで定義されたグローバル変数は互いに透明であり,Aをコンパイルする際にBにもiが定義されていることに気づかず,同様にBをコンパイルする際にAにもiが定義されていることに気づかない.ただし、リンクフェーズでは、各ファイルの内容を「統合」するため、一部のファイルで定義されているグローバル変数名が同じであれば、この時点でエラー、すなわち、上記の繰り返し定義のエラーが発生します.したがって、各ファイルで定義されているグローバル変数名は同じではありません.
しかし、B.cppでiRIを定義する場合、A.cppで直接使用します.A.cppのコンパイル中にパスできません.
//A.cpp
int main()
{
iRI=64;
}

//B.cpp
int iRI;

gcc A.cpp -c
iRI was not declared in this scope.
 
  
 
         因为编译器按照文件方式编译,所以编译A.cpp时,并不知道B.cpp中定义了iRI。也就是说:文件中定义的全局变量的可见性扩展到整个程序是在链接完成之后,而在编译阶段,他们的可见性仍局限于各自的文件。 
  
 

        解决方案如下:编译器的目光不够长远,编译器没有能够意识到,某个变量符号虽然不是本文件定义的,但是它可能是在其它的文件中定义的,虽然编译器不够远见,但是我们可以给它提示,帮助它来解决上面出现的问题。这就是extern的作用了,extern的原理很简单,就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,你要放行!”

//A.cpp:
extern int iRI;
int main()
{
iRI = 64;
//.....
}

//B.cpp
int iRI;

これでコンパイルは通過できます.extern int iRI;//スペースは割り当てられていません.コンパイラに通知するだけで、他のファイルでiRIが定義されています.
extern役割2:C++ファイルでC方式コンパイルを呼び出す関数
C方式コンパイルとC++方式コンパイルは、Cに対して、C++にリロードなどの新しい特性が追加されました.したがって、グローバル変数と関数名のコンパイル後のネーミング方法には大きな違いがあります.int a; int functionA(); C方式コンパイルの場合:int a;=>a int functionA(); => _functionA対C++方式コンパイル:int a;=>xx@xxx@a int functionA(); => xx@xx@functionAでは、リロードをサポートするため、C++方式でコンパイルすると、生成されるグローバル変数名と関数名が複雑になることがわかります.C方式でコンパイルした下線とは異なります.そこで、次のような状況があります.
例2:C++はC++定義のグローバル変数//A.cpp:extern int iRIを呼び出す;int main() { iRI = 64;//..... }//B.cpp int iRI; gcc A.cpp-c gcc B.cpp-c gcc A.o B.o-o testならリンクをコンパイルしても問題ありません.例3:C++はC定義のグローバル変数//A.cppを呼び出す:extern int iRI;int main() { iRI = 64;//..... }//B.c int iRI; コンパイル時は問題なく、gcc A.cpp-c gcc B.c-cですがリンクすると、gcc B.o-o testはiRIが定義していないと報告します.どうしてですか.gccはA.cppを見るので、C++方式でコンパイルし、B.cを見ると、C方式でコンパイルします.だからA.cppでのiRI=>XXX@XXX_iRI; 一方、B.cではiRI=〉iRI; だからリンクする時、A.cppは探し当てたいですXXX@XXX_iRIああ、もちろん見つからない.だから、iRIはC方式でコンパイルされていることをコンパイラに伝える必要があります.//A.cpp: extern "C"{ int iRI; } int main() { iRI = 64;//..... }//B.c int iRI; このように,A.cppをコンパイルすると,コンパイラはiRIがC方式でコンパイルされていることを知る.使用されますiRI.このようにB.cが提供する_iRIはA.cppで見つけることができます.例4:C++はC定義のfunction//A.cpp extern int functionA()を呼び出す.int main() { functionA(); } //B.c int functionA() {//.... } gcc A.cpp-c gcc B.c-cは問題ありません.しかし、同様に、gcc A.o B.o-o testはエラーを報告し、functionA()が見つからない.これは,gccがA.cppをC++方式でコンパイルし,B.cがC方式でコンパイルしているためである.したがってfunctionAはB.cでは:functionA. A.cppでは:XX@XXX_functionAだからリンクする時A.cppは探し出せませんXX@XX_function.するとコンパイラに通知する必要があります.functionA()はC方式でコンパイルされて名前が付けられています.//A.cpp extern "C"{ int functionA(); } int main() { functionA(); } //B.c int functionA() {//.... } すると、コンパイルリンクはすべて通過できます.まとめ:extern“C”{functionA();}//声明だけでなく、このfunctionはC方式でコンパイルしてください.だから再びexternを必要としない.extern"C"{ extern functionA(); }//このようにするのはあまり意味がない.