C/C++のexternキーワードの詳細説明

高洛峰
リリース: 2016-12-19 14:32:19
オリジナル
1086 人が閲覧しました

1 基本的な説明: extern は変数または関数の前に配置して、変数または関数の定義が別のファイルにあることを示し、コンパイラーがこの変数または関数に遭遇したときに他のモジュールでその定義を見つけるように促します。また、extern はリンク指定にも使用できます。

つまり、extern には 2 つの関数があります。まず、extern "C" void fun(int a, int b); のように、"C" と一緒に使用すると、変換時に関数 fun をコンパイルするように指示されます。関数名は、C++ ではなく C の規則に従って、関数名を変換すると、認識できない名前に変更されます。これは、fun@aBc_int_int#%$ などになります。これはコンパイラの「性質」に依存します (コンパイラごとに異なる方法が採用されています)。C++ は関数のオーバーロードをサポートしているため、ここではあまり説明しません。興味がある場合は検索してください。オンラインで、満足のいく説明が得られると思います。
次に、ヘッダー ファイルなどで extern が変数または関数を変更するために使用されない場合、その機能は関数を宣言することです。 function グローバル変数のスコープ キーワード、それによって宣言された関数および変数は、このモジュールまたは他のモジュールで使用できます。つまり、モジュール B (コンパイル ユニット) が参照する場合は、宣言であることに注意してください。モジュール (コンパイル単位) A でグローバル変数または関数が定義されている場合、モジュール A のヘッダー ファイルをインクルードするだけで済みます。コンパイル段階では、モジュール B は関数または変数を見つけることができませんが、エラーは報告されません。接続時にモジュール A から関数または変数を取得します。この関数は生成されたオブジェクト コード内にあります。

2 質問: extern 変数
ソースファイルで配列が定義されています: char a[6];
別のファイルで次のステートメントで宣言されています: extern char *a;
すみません、これでいいですか?
答えと分析:
1)、いいえ、プログラムは実行中に不正アクセスを通知します。その理由は、型 T へのポインターは型 T の配列と同等ではないためです。 extern char *a は文字配列ではなくポインター変数を宣言しているため、実際の定義とは異なり、実行時に不正アクセスが発生します。宣言を extern char a[ ] に変更する必要があります。
2) 解析例は以下の通り a[] = "abcd" の場合、外部変数 a=0x61626364 (abcd の ASCII コード値)、*a は明らかに無意味です
明らかに a が指すスペース (0x61626364)。意味がなく、不正なメモリアクセスが発生しやすい。
3) これは、extern を使用する場合、宣言の形式に厳密に対応する必要があることを思い出させます。実際のプログラミングでは、このようなエラーはよくあります。
4) extern は変数宣言でよく使われます。このグローバル変数を参照したい場合は、 *.h に入れて extern で宣言します。

3 問題: extern 関数のプロトタイプが変更された場合
関数提供者が関数のプロトタイプを一方的に変更した場合、ユーザーがそれを知らずに元の extern 宣言を使い続けた場合、コンパイラはコンパイル中にエラーを報告しません。しかし、運用中に入力パラメータの不足または過剰によりシステムエラーが発生することがよくありますが、この状況はどのように解決すればよいでしょうか。
回答と分析:
現在、業界にはこの状況に対処するための完璧なソリューションがありません。通常のアプローチは、プロバイダーが独自の xxx_pub.h で外部インターフェイスの宣言を提供し、呼び出し元がヘッダーをインクルードすることです。ファイルなので、extern ステップをスキップします。このエラーを回避するには。
剣には両刃があり、externの適用には状況に応じて異なる方法を選択する必要があります。

4 質問: extern "C"
C++ 環境で C 関数を使用する場合、コンパイラーが obj モジュールで C 関数定義を見つけられず、リンクが失敗することがよくあります。この状況を解決するにはどうすればよいですか?

答えと分析:
コンパイル時に関数のポリモーフィズムの問題を解決するために、C++言語は関数名とパラメータを組み合わせて中間関数名を生成しますが、C言語はそうではないので、途中で見つかりません。対応する関数の場合、この時点で C 関数を extern "C" でリンクする必要があります。これはコンパイラに、私の名前を保持し、リンク用の中間関数名を生成しないように指示します。
以下が標準的な書き方です:
//.hファイルの先頭
#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */


// .h ファイルの終了場所
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */

5 質問: extern 関数宣言
extern は関数の前に置かれ、関数宣言の一部になることがよく見られます。では、C 言語のキーワード extern は関数の宣言においてどのような役割を果たしますか。
答えと分析:
キーワード extern が関数の宣言に含まれている場合、それはその関数が他のソース ファイルで定義できることを意味するだけで、他の効果はありません。つまり、次の 2 つの関数宣言の間に明らかな違いはありません:
extern int f(); と int f();
もちろん、この種の使用法は依然として存在します。つまり、次の関数の代わりにプログラム内で関数を宣言します。 include "*.h"。一部の複雑なプロジェクトでは、すべての関数宣言の前に extern 変更を追加することに慣れています。これを行う理由と長所と短所は、次の例で見ることができます: 「extern で変更されたグローバル変数」

(1) test1.h には次のステートメントがあります:
#ifndef TEST1H
#define TEST1H
extern char g_str[]; //グローバル変数g_str
を宣言 void fun1();
#endif
(2) test1.cpp内 #include "test1.h"
char g_str[] = "123456"; variable g_str
void fun1() { cout << g_str << }
(3) 上記は test2 モジュールもあり、コンパイルして接続することができます。 g_str、元のファイルに追加するだけです
#include "test1.h"

void fun2() { cout << g_str << }

上記の test1 と test2興味がある場合は、ultraEdit を使用して test1.obj を開くと、文字列「123456」が見つかりますが、test2.obj には見つかりません。はプロジェクト全体のグローバル変数であり、メモリ内には 1 つのコピー test2 のみが存在します。.obj コンパイル ユニットの別のコピーは必要ありません。そうしないと、接続中に重複定義エラーが報告されます。
(4)定義忘れを防ぐために、グローバル変数の宣言と定義をまとめて記述することを好む人もいます。たとえば、上記の test1.h を
extern char g_str[] = "123456"; に変更します。 extern はありません
次に、test1.cpp の g_str の定義を削除し、2 つのモジュール test1 と test2 をコンパイルして接続すると、ヘッダー ファイルの後にグローバル変数 g_str の定義を配置したため、接続エラーが報告されます。 test1.cpp モジュールには test1.h が含まれているため、g_str が一度定義され、test2.cpp にも test1.h が含まれているため、g_str が再度定義されます。今回は、コネクタは test1 と test2 を接続するときに 2 つの g_str を見つけます。 g_str の定義を test1.h に含める必要がある場合は、test2 のコードから #include "test1.h" を削除し、次のように置き換えます。
extern char g_str[];
void fun2() { cout < g_str << endl; }
現時点では、コンパイラは g_str が外部コンパイル モジュールであることを認識しているため、このモジュールではそれを再度定義しません。これは、#include を使用できないためです。 test2.cpp 内の「test1.h」では、extern で変更しない限り、test1.h で宣言された他の関数を使用できません。この場合、宣言した関数だけが長いリストになり、ヘッダー ファイルの関数になります。外部使用のためのインターフェイスを提供することです。そのため、ヘッダー ファイル内で宣言を行うだけであることを覚えておいてください。真実は常に単純です。

6. extern と static

(1) extern は、変数が別の場所で定義されており、その変数をここで使用することを示します。

(2) static は、メモリを割り当てるときに、静的領域に格納されます。

静的スコープは内部的に接続されており、オブジェクト自体とは別個に保存され、extern も別個に保存されますが、extern を使用して他のオブジェクトから参照することもできます。 static は使用できず、オブジェクト自体のみが使用を許可されます。具体的な違いは、第一に、static と extern は「互換性のない」ペアであることです。つまり、extern と static は同時に変数を変更できません。 、 static によって変更されたグローバル変数の宣言は、定義が同時に実行されることとは異なります。つまり、 static を使用してヘッダー ファイルでグローバル変数を宣言すると、その変数も同時に定義されます。静的に変更されたグローバル変数のスコープは、独自のコンパイル単位のみにすることができます。つまり、スコープ「Global」はこのコンパイル単位に対してのみ有効であり、他のコンパイル単位からは参照できません。たとえば、

(1) test1.h:
#ifndef TEST1H
#define TEST1H
static char g_str[] = "123456"
void fun1 ();
#endif
;

(2) test1.cpp:
#include "test1.h"
void fun1() { cout < (3) test2.cpp
#include "test1.h"
void fun2() { cout << g_str << endl; }
test1.obj を開くと、文字列「123456」が見つかります。これらは、test2.obj でも見つかります。重複定義エラーが報告されずに正常に接続できる理由は、2 つの異なる変数の割り当てと同じように、内容は同じですが、格納されている物理アドレスが異なるためです。は同じ値を持ち、これら 2 つの変数はそれぞれのコンパイル単位で動作します。 おそらく、あなたはもっと真剣に、上記のコードをこっそりトレースしてデバッグした結果、2 つのコンパイル単位 (test1、test2) の g_str のメモリ アドレスが同じであることがわかり、静的に変更された変数も動作できると結論付けるでしょう。他のモジュールについても説明していますが、コンパイラはあなたを騙していると言いたいのです。ほとんどのコンパイラは、さまざまなモジュールを接続するときにメモリを節約し、実行効率を高めるターゲット プログラムを生成するという目標を達成するためのコードの最適化機能を備えています。場合によっては、上記の「123456」など、同じ内容を持つメモリのコピーが 1 つだけコピーされるため、2 つのコンパイル ユニットにある変数は同じ内容を持つため、接続時にメモリにコピーが 1 つだけ存在します。ここで、上記のコードを次のように変更すると、コンパイラの嘘をすぐに暴露できます:
(1) test1.cpp:
#include "test1.h"
void fun1()
{
g_str[ 0] = '' a '';
Cout & lt; & lt; g_str & lt; endl;}}

#include "test1.h"

void fun2 () ; & lt ; g_str << }
(3) void main() {
fun2() // 123456
; 2 コンパイル単位の g_str アドレスが同じではありません。1 か所で変更したため、コンパイラは強制的に元のメモリに復元されます。2 つのモジュールの変数のコピーがメモリ内に 2 つあります。 static には上記の特性があるため、他のモジュールに不要な情報を汚染しないように、static グローバル変数を定義する場合はヘッダー ファイルではなく元のファイルに配置するのが一般的です。

7. extern と const

C++ の const で変更されたグローバル定数は、static と同じ特性を持っています。つまり、このコンパイル済みモジュール内でのみ使用できますが、const を extern とともに使用して定数が可能であることを宣言できます。 extern const char g_str[]; などのモジュール内で使用できます: const char g_str[] = "123456";

したがって、const が使用される場合は、それを元のファイルで定義することを忘れないでください。単体ではstaticと同じで、externと併用すると連携時もexternと特性が同じになります!したがって、const についてはあまり言う必要はありません。const char* g_str = "123456" は const char g_str[] = "123465" とは異なるということを思い出していただきたいと思います。その g_str は定数ではなく、定義されたグローバル変数 (他のコンパイル単位で使用できる) とみなされます。そのため、char*g_str を const グローバル定数の規則に準拠させたい場合は、次のように const を定義するのが最善です。 char* const g_str="123456".



C/C++ の extern キーワードに関するその他の関連記事については、PHP 中国語 Web サイトに注目してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のおすすめ
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート