これは、Windows と Unix 系システムのかなり有名な違いです。
何があっても:
- 各プロセスには独自のアドレス空間があります。つまり、プロセス間でメモリが共有されることはありません (プロセス間通信ライブラリまたは拡張機能を使用しない限り)。
- One Definition Rule (ODR) は引き続き適用されます。つまり、リンク時に表示できるグローバル変数の定義は 1 つだけです (静的または動的リンク)。
したがって、ここでの重要な問題は実際には可視性です。
いずれの場合も、static
グローバル変数 (または関数) がモジュール (dll/so または実行可能ファイル) の外側から見えることはありません。C++ 標準では、これらが内部リンケージを持っている必要があります。つまり、それらが定義されている翻訳単位 (オブジェクト ファイルになる) の外では見えません。それで、それはその問題を解決します。
extern
複雑になるのは、グローバル変数がある場合です。ここで、Windows と Unix ライクなシステムは完全に異なります。
Windows (.exe および .dll) の場合、extern
グローバル変数はエクスポートされたシンボルの一部ではありません。つまり、異なるモジュールは、他のモジュールで定義されたグローバル変数をまったく認識しません。これは、たとえば、extern
DLL で定義された変数を使用することになっている実行可能ファイルを作成しようとすると、リンカー エラーが発生することを意味します。これは許可されていないためです。オブジェクト ファイル (または静的ライブラリ) にその extern 変数の定義を提供し、それを実行可能ファイルと DLL の両方に静的にリンクする必要があります。その結果、2 つの異なるグローバル変数 (1 つは実行可能ファイルに属し、もう 1 つは DLL に属します) になります。 )。
Windows でグローバル変数を実際にエクスポートするには、関数のエクスポート/インポート構文に似た構文を使用する必要があります。
#ifdef COMPILING_THE_DLL
#define MY_DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define MY_DLL_EXPORT extern "C" __declspec(dllimport)
#endif
MY_DLL_EXPORT int my_global;
これを行うと、グローバル変数がエクスポートされたシンボルのリストに追加され、他のすべての関数と同様にリンクできます。
Unix ライクな環境 (Linux など) の場合、拡張子を持つ「共有オブジェクト」と呼ばれる動的ライブラリは、すべてのグローバル変数 (または関数)を.so
エクスポートします。extern
この場合、任意の場所から共有オブジェクト ファイルへのロード時リンクを実行すると、グローバル変数が共有されます。つまり、1 つにリンクされます。基本的に、Unix ライクなシステムは、静的ライブラリと動的ライブラリのリンクにほとんど違いがないように設計されています。繰り返しになりますが、ODR は全面的に適用されます。extern
グローバル変数はモジュール間で共有されます。つまり、ロードされたすべてのモジュールで 1 つの定義のみを持つ必要があります。
最後に、どちらの場合も、Windows または Unix ライクなシステムの場合、動的ライブラリの実行時リンクを実行できます。つまり、LoadLibrary()
/ GetProcAddress()
/FreeLibrary()
またはdlopen()
/ dlsym()
/のいずれかを使用しますdlclose()
。その場合、使用したい各シンボルへのポインターを手動で取得する必要があり、これには使用したいグローバル変数が含まれます。グローバル変数の場合、グローバル変数がエクスポートされたシンボル リストの一部である場合 (前の段落の規則により)、関数の場合と同じようにGetProcAddress()
orを使用できます。dlsym()
そしてもちろん、必要な最後の注意として、グローバル変数は避けるべきです。そして、あなたが引用したテキスト(「不明確」であることについて)は、私が説明したばかりのプラットフォーム固有の違いを正確に指していると思います(動的ライブラリは実際にはC++標準によって定義されていません。これはプラットフォーム固有の領域です。信頼性/移植性がはるかに低くなります)。