Cでexternキーワードを正しく使用する方法


235

私の質問は、関数がexternC のキーワードで参照される場合についてです。

これが実際にいつ使用されるべきかはわかりません。私がプログラムを書いているとき、私が使用するすべての機能は、インクルードしたヘッダーファイルを介して利用できるようになります。それではextern、ヘッダーファイルで公開されていないものにアクセスすると便利なのはなぜでしょうか。

私はどのようにextern正しく動作しないか考えているかもしれません。もしそうなら私を訂正してください。

編集:はずですextern何か、それはヘッダファイルにキーワードを指定せずにデフォルトの宣言ですか?


回答:


290

extern」はリンケージを変更します。キーワードを使用すると、関数/変数は他の場所で使用できると見なされ、解決はリンカーに委ねられます。

関数と変数の「extern」には違いがあります。変数では、変数自体をインスタンス化しません。つまり、メモリを割り当てません。これは別の場所で行う必要があります。したがって、他の場所から変数をインポートする場合は重要です。関数の場合、これはリンケージがexternであることをコンパイラーに通知するだけです。これはデフォルトなので(関数が外部リンケージを使用してバインドされていないことを示すためにキーワード "static"を使用します)、明示的に使用する必要はありません。


1
次に、同じexternがGitにある理由:非常に人気のある最新のソフトウェアで確認する:github.com/git/git/blob/master/strbuf.h
rsjethani

K&Rは、関数を「extern」として宣言することがデフォルトであることに注意していませんが、この答えは私の混乱を解決します!
容認者、2014

@rsjethaniこれは、ドキュメントをより厳密でフォーマット化することだと思います。
容認者、2014

馬鹿げた質問かもしれませんが、これはフォワード宣言とどのように比較できますか?
weberc2 2015年

196

externは、このデータがどこかで定義されており、リンカーに接続されることをコンパイラーに伝えます。

ここでの応答の助けを借りて、ここで数人の友人と話すことは、externの実際の使用例です。

例1-落とし穴を示すには:

File stdio.h:

int errno;
/* other stuff...*/

myCFile1.c:
#include <stdio.h>

Code...

myCFile2.c:
#include <stdio.h>

Code...

myCFile1.oとmyCFile2.oがリンクされている場合、各cファイルにはerrnoの個別のコピーがあります。リンクされたすべてのファイルで同じerrnoが使用できるはずなので、これは問題です。

例2-修正。

File stdio.h:

extern int errno;
/* other stuff...*/

File stdio.c

int errno;

myCFile1.c:
#include <stdio.h>

Code...

myCFile2.c:
#include <stdio.h>

Code...

ここで、myCFile1.oとMyCFile2.oの両方がリンカーによってリンクされている場合、両方が同じerrnoを指します。したがって、externで実装を解決します。


70
問題は、myCFile1およびmyCFile2モジュールにerrnoの別個のコピーがあることではなく、どちらも「errno」と呼ばれるシンボルを公開していることです。リンカはこれを見ると、どの "errno"を選択すべきかわからないため、エラーメッセージを表示して救済します。
cwick

2
「リンカによってリンクされている」とは実際にはどういう意味ですか?誰もがこの用語を使用していますが、定義は見つかりません:(
Marcel Falliere

7
@MarcelFalliere ウィキ〜コンパイラが独自に各ソースファイルをコンパイルし、ソースファイルごとにオブジェクトファイルを作成します。リンカーは、これらのオブジェクトファイルを1つの実行可能ファイルにリンクします。
Bitterblue 2013年

1
@cwick gccは-Walland を使用し-pedanticた後でもエラーや警告を出しません。どうして ?そしてどうやって ?
b-ak

6
インクルードガードはこれとまったく同じことから保護しませんか?
obskyr 2017

32

externキーワードは関数にとって冗長であると既に述べられています。

コンパイルユニット間で共有される変数については、ヘッダーファイルでexternキーワードを使用して宣言し、externキーワードを使用せずに単一のソースファイルで定義する必要があります。単一のソースファイルは、ベストプラクティスとして、ヘッダーファイルの名前を共有するものである必要があります。


@aib「関数に冗長」、bluebrotherの回答で私のコメントを確認してください。
rsjethani 2013

ヘッダーファイルの関数を公開したくない場合はどうしますか?1つのCファイルで変数を宣言し、別のCファイルでexternを使用してアクセスする方がよいでしょう。リンカーに問題を解決させ、残りのヘッダーを非表示にします。
ste3e 2014年

16

何年か後、私はこの問題を発見しました。すべての回答とコメントを読んだ後、いくつかの詳細を明確にできると思いました...これは、Google検索でここにたどり着く人にとって便利かもしれません。

問題は特に「extern」関数の使用に関するものであるため、グローバル変数での「extern」の使用は無視します。

3つの関数プロトタイプを定義しましょう:

//--------------------------------------
//Filename: "my_project.H"
extern int function_1(void);
static int function_2(void);
       int function_3(void);

ヘッダーファイルは、次のようにメインソースコードで使用できます。

//--------------------------------------
//Filename: "my_project.C"
#include "my_project.H"

void main(void){
    int v1 = function_1();
    int v2 = function_2();
    int v3 = function_3();
}

int function_2(void) return 1234;

コンパイルしてリンクするには、その関数を呼び出すのと同じソースコードファイルで "function_2"を定義する必要があります。他の2つの関数は、異なるソースコード " .C"で定義することも、ソースコードがないバイナリファイル( .OBJ、*。LIB、*。DLL)に配置することもできます。

違いをよく理解するために、別の「* .C」ファイルにヘッダー「my_project.H」を再度含めてみましょう。同じプロジェクトで、次のファイルを追加します。

//--------------------------------------
//Filename: "my_big_project_splitted.C"
#include "my_project.H"

void old_main_test(void){
    int v1 = function_1();
    int v2 = function_2();
    int v3 = function_3();
}

int function_2(void) return 5678;

int function_1(void) return 12;
int function_3(void) return 34;

注意すべき重要な機能:

  • ヘッダーファイルで関数が「静的」として定義されている場合、コンパイラ/リンカーは、そのインクルードファイルを使用する各モジュールでその名前の関数のインスタンスを見つける必要があります。

  • Cライブラリの一部である関数は、プロトタイプをそのモジュールでのみ「静的」に再定義することにより、1つのモジュールでのみ置き換えることができます。たとえば、「malloc」および「free」の呼び出しを置き換えて、メモリリーク検出機能を追加します。

  • 指定子「extern」は関数には実際には必要ありません。「静的」が見つからない場合、関数は常に「外部」であると見なされます。

  • ただし、「extern」は変数のデフォルトではありません。通常、多くのモジュールで表示される変数を定義するヘッダーファイルでは、「extern」を使用する必要があります。唯一の例外は、ヘッダーファイルが1つだけのモジュールからインクルードされることが保証されている場合です。

    多くのプロジェクトマネージャーは、このような変数をヘッダーファイル内ではなく、モジュールの先頭に配置する必要があります。ビデオゲームエミュレーター「Mame」などの一部の大規模プロジェクトでは、そのような変数は、それらを使用する最初の関数の上にのみ出現する必要があります。


では、なぜ静的関数は外部定義に対して定義を必要とするのでしょうか?(私はこれが2年遅れていることを知っていますが、これは実際に理解する
のに

2
この定義は、関数を100行目で呼び出し、500行目でインスタンス化する場合に必要です。100行目は未定義のプロトタイプを宣言します。したがって、上部にプロトタイプを追加します。
Christian Gingras

15

Cでは、プロトタイプが他の場所で定義されている関数を宣言しているため、関数プロトタイプには「extern」が含まれています。つまり、関数プロトタイプにはデフォルトで外部リンケージがあります。'extern'の使用は問題ありませんが、冗長です。

(静的リンケージが必要な場合、プロトタイプと関数ヘッダーの両方で関数を「静的」として宣言する必要があります。これらは通常、同じ.cファイルにある必要があります)。


8

externキーワードとその例について紹介した非常に優れた記事:http : //www.geeksforgeeks.org/understanding-extern-keyword-in-c/

extern関数宣言での使用が冗長であることには同意しませんが。これはコンパイラ設定であることになっています。したがって、extern必要な場合は関数宣言でを使用することをお勧めします。


3
ここに来る前にgeeksforgeeks.orgの記事を読んだことがあるのですが、かなり下手に書かれていることに気づきました。文法および構文の欠点は別として、多くの単語を使用して同じポイントを数回作成し、重要な情報を読み飛ばします。たとえば、例4では、突然「somefile.h」が含まれていますが、「somefile.hにvarの定義があると仮定する」以外は何も言われていません。さて、私たちが「想定している」情報は、たまたま私が探している情報です。残念ながら、このページの回答はどれも優れています。
エリーゼファンルーイ

6

プログラム内の各ファイルが最初にオブジェクトファイルにコンパイルされる場合、オブジェクトファイルは一緒にリンクされますextern。それはコンパイラに「この関数は存在しますが、そのコードはどこかにあります。慌てないでください」と伝えます。


ええと、これが通常の変換方法です。ソースファイルはオブジェクトファイルにコンパイルされ、リンクされます。その場合、あなたはいつexternを必要としませんか?また、#includeを使用して関数を取得するのではなく、関数のプロトタイプを取得します。私はあなたが何を話しているのか理解できません。
David Thornley、

最近、この問題を誤解しているようです。申し訳ありません。Cを初めて使用したときは、#include "file.c"を使用して、1つのファイルの関数を他のファイルに直接インクルードしました。次に、「extern」の使い方を見つけました。彼は私と同じ過ちを犯していると思いました。
Chris Lutz、

4

ヘッダーファイル内の関数と変数の宣言はすべてにする必要がありますextern

この規則の例外は、ヘッダーで定義されたインライン関数と、ヘッダーで定義されていても、翻訳単位(ヘッダーが含まれるソースファイル)に対してローカルでなければならない変数ですstatic。これらは、でなければなりません。

ソースファイルでexternは、ファイルで定義されている関数や変数に使用しないでください。ローカル定義の前に接頭辞を付けるだけでstatic、共有定義に対しては何もしません。デフォルトでは、外部シンボルになります。

externソースファイルで使用する唯一の理由は、他のソースファイルで定義され、ヘッダーファイルが提供されていない関数と変数を宣言することです。


関数プロトタイプの宣言externは実際には不要です。スペースを浪費するだけで関数宣言が行の制限をオーバーフローする傾向があるため、一部の人々はそれを嫌います。このように、関数と変数を同じように扱うことができるので、他の人はそれを好みます。


「ヘッダーファイル内の関数と変数のすべての宣言をexternにする必要がある」理由を教えてください。他の応答からは、デフォルトでexternであることがわかります。
lillq 2009年

@レーン:extern関数宣言ではオプションですが、変数と関数を同じように扱うのが好きです-少なくとも、これが私がこれを始めた理由を正確に覚えていないため、思いつくことができる最も合理的なことです;)
クリストフ

ヘッダーを含む他のランダムなCファイルから見えないように、常にCファイルにグローバル変数を含めるのは良い考えではありませんか。また、明確にするために、初期化された真のシンクを除くすべてのグローバルで常にexternを使用します。プレフィックスがexternの場合は、別の場所で定義されます。
ste3e 2014年

3

他のソースファイルで実際に定義されている関数、ヘッダーでのみ宣言する必要があります。この場合、ヘッダーでプロトタイプを宣言するときにexternを使用する必要があります。

ほとんどの場合、関数は次のいずれかになります(ベストプラクティスに似ています)。

  • 静的(その.cファイルの外には表示されない通常の関数)
  • 静的インライン(.cまたは.hファイルからのインライン)
  • extern(次の種類のヘッダーでの宣言(下記参照))
  • [キーワードは一切ありません](extern宣言を使用してアクセスするための通常の関数)

これがデフォルトの場合、プロトタイプを宣言するときになぜexternするのですか?
lillq 2009年

@レーン:少し偏ったかもしれませんが、私が取り組んだすべての正気なプロジェクトは次の規則を使用します:ヘッダーで、外部関数(したがってextern)に対してのみプロトタイプを宣言します。.cファイルでは、プレーンプロトタイプを使用して特定の順序付けの必要性を取り除くことができますが、ヘッダーに配置しないでください。
エデュアルド-ガブリエルムンテアヌ

1

その関数が別のdllまたはlibで定義されている場合、コンパイラーはそれを見つけるためにリンカーに委ねます。一般的なケースは、OS APIから関数を呼び出す場合です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.