[c&cpp]CC++インタラクション-extern"C"

13710 ワード

   1:  #ifndef __INCvxWorksh
   2:  #define __INCvxWorksh
   3:  #ifdef __cplusplus
   4:  extern "C" {
   5:  #endif
   6:  /*...*/
   7:  #ifdef __cplusplus
   8:  }
   9:  #endif
  10:  #endif /* __INCvxWorksh */

<!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
-->
ヘッダファイルにおけるコンパイルマクロ「#ifndef__INCvxWorksh、#define__INCvxWorksh、#endif」の役割は、ヘッダファイルの重複参照を防止することであることは明らかである.
3-9行のコードの役割は何ですか?
1:extern「C」の意味
extern「C」は二重の意味を含み、字面から見ると、まず、それに修飾された目標は「extern」である.次に、その修飾の目標は「C」である.extern「C」によって定義される関数または変数はexternタイプである.
externは、コンパイラに宣言された関数および変数が本モジュールまたは他のモジュールで使用できることを示すC/C++言語の関数およびグローバル変数の作用範囲(可視性)を示すキーワードです.
文:extern int a;変数aを定義しているわけではなく、aにメモリ領域を割り当てていない変数の宣言にすぎません.変数aは、すべてのモジュールでグローバル変数として一度しか定義できません.そうしないと、リンクエラーが発生します.通常、このモジュールが他のモジュールに参照される関数およびグローバル変数は、モジュールのヘッダファイルでキーワードexternで宣言されます.例えば、モジュールBがモジュールAで定義されたグローバル変数と関数を参照しようとする場合、モジュールAのヘッダファイルを含めるだけでよい.このように、モジュールBでモジュールAの関数を呼び出すと、コンパイル段階では、モジュールBはその関数が見つからないが、エラーは報告されない.この関数は、モジュールAのコンパイルによって生成された接続フェーズのターゲットコードから検出されます.externに対応するキーワードはstaticであり、修飾されたグローバル変数と関数は本モジュールでのみ使用できます.したがって、1つの関数または変数が本モジュールでのみ使用可能である場合、extern「C」によって修飾されることはできません.extern「C」で修飾された変数と関数はC言語でコンパイルされ接続されている.
2:extern"C"宣言を付けないときのコンパイル方式まずC++の中でCのような関数がどのようにコンパイルされているかを見てみましょう.オブジェクト向け言語として、C++は関数のリロードをサポートし、プロシージャ言語Cはサポートしません.関数がC++によってコンパイルされたシンボルライブラリの名前はC言語とは異なる.例えば、ある関数のプロトタイプをvoid foo(int x,int y);この関数はCコンパイラによってコンパイルされたシンボルライブラリの名前は_foo、C++コンパイラは像_を生成しますfoo_int_intなどの名前(コンパイラによって生成される名前は異なるかもしれませんが、同じメカニズムを採用しており、生成される新しい名前は「mangled name」と呼ばれています).foo_int_intという名前には,関数名,関数パラメータ数,タイプ情報が含まれており,C++はこのようなメカニズムによって関数リロードを実現している.例えば、C++では、関数void foo(int x,int y)とvoid foo(int x,float y)のコンパイルによって生成される符号は異なり、後者は_foo_int_float.
同様に、C++の変数は、ローカル変数に加えてクラスメンバー変数およびグローバル変数もサポートされる.ユーザが作成したプログラムのクラスメンバー変数は、グローバル変数と同名である可能性があります.を選択します.本質的には、コンパイラはコンパイルを行う際に、関数の処理と同様に、クラス内の変数にユニークな名前を付け、この名前はユーザープログラムで同名のグローバル変数の名前とは異なる.
3:extern"C"宣言を付けない場合のリンク方式C++において、モジュールAのヘッダファイルは以下のように仮定する.
   1:  //   A    moduleA.h
   2:  #ifndef MODULE_A_H
   3:  #define MODULE_A_H
   4:  int foo( int x, int y );
   5:  #endif
   6:   
   7:  //    B      :
   8:   
   9:  //   B     moduleB.cpp
  10:  #include "moduleA.h"
  11:  foo(2,3);

<!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
-->
実際、リンクフェーズでは、モジュールAから生成するターゲットファイルmoduleAがリンク器によって生成される.objで探す_foo_int_intという記号
4:extern"C"宣言後のコンパイルとリンク方式
extern"C"宣言を加えると、モジュールAのヘッダファイルは次のようになります.
   1:  //   A    moduleA.h
   2:  #ifndef MODULE_A_H
   3:  #define MODULE_A_H
   4:  extern "C" int foo( int x, int y );
   5:  #endif

<!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
-->
モジュールBの実装ファイルではfoo(2,3)が呼び出されたが、その結果、1)モジュールAがfooを生成するターゲットコードをコンパイルする際に、その名前を特別に処理せず、C言語方式を採用した.2)コネクタは、モジュールBのターゲットコードのfoo(2,3)呼び出しを探しているときに、変更されていないシンボル名を探している_foo.
モジュールAにおいて関数がfooをextern"C"タイプと宣言し、モジュールBにextern int foo(int x,int y)が含まれている場合、コネクタがmoduleAから検出されるため、モジュールBにはモジュールAの関数が見つからない.objで探す_foo_int_intですがmoduleA.objにはシンボル名のみfoo、逆も同じです.
5.extern「C」の慣用1)C++でC言語の関数と変数を参照する
C言語ヘッダファイル(cExample.hと仮定)を含む場合は、次の処理を行います.
   1:  extern "C" {
   2:     #include "cExample.h"
   3:  }

<!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
-->
一方、C言語のヘッダファイルでは、その外部関数はexternタイプとしてしか指定できず、C言語ではextern「C」宣言はサポートされていない.cファイルにextern「C」が含まれている場合、コンパイル構文エラーが発生します.
サンプルコードは次のとおりです.
   1:  /* c     :cExample.h */
   2:  #ifndef C_EXAMPLE_H
   3:  #define C_EXAMPLE_H
   4:  extern int add(int x,int y);
   5:  #endif
   6:   
   7:  /* c      :cExample.c */
   8:  #include "cExample.h"
   9:  int add( int x, int y ) {
  10:  return x + y;
  11:  }
  12:   
  13:  // c++    ,  add:cppFile.cc
  14:  extern "C"  {
  15:  #include "cExample.h"
  16:  }
  17:   
  18:  int main(int argc, char* argv[]) {
  19:      add(2,3); 
  20:      return 0;
  21:  }

<!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
-->
C++がC言語で記述されたdllを呼び出す場合、dllのヘッダファイルまたは宣言インタフェース関数を含む場合はextern"C"{}を追加します.2). CでC++言語を参照する関数と変数
C++のヘッダファイルにはextern"C"を追加する必要がありますが、C言語ではextern"C"を宣言したヘッダファイルを直接参照することはできません.CファイルではC++で定義したextern"C"関数のみをexternタイプとして宣言する必要があります.
サンプルコードは次のとおりです.
   1:  //C++    cppExample.h
   2:  #ifndef CPP_EXAMPLE_H
   3:  #define CPP_EXAMPLE_H
   4:  extern "C" int add( int x, int y );
   5:  #endif
   6:   
   7:  //C++     cppExample.cpp
   8:  #include "cppExample.h"
   9:  int add( int x, int y ) {
  10:      return x + y;
  11:  }
  12:   
  13:  /* C     cFile.c
  14:  /*        :#include "cExample.h" */
  15:  extern int add( int x, int y );
  16:  int main( int argc, char* argv[] ) {
  17:      add( 2, 3 ); 
  18:      return 0;
  19:  }