C 関数に対する extern キーワードの影響

foo.c と bar.c の 2 つのファイルがあります。

ここにfoo.cがあります

#include <stdio.h>

volatile unsigned int stop_now = 0;
extern void bar_function(void);

int main(void)
{
  while (1) {
     bar_function();
     stop_now = 1;
  }
  return 0;
}

さて、ここに bar.c があります

#include <stdio.h>

extern volatile unsigned int stop_now;

void bar_function(void)
{
   if (! stop_now) {
      printf("Hello, world!\n");
      sleep(30);
   }
}

ご覧のとおり、 foo.c と bar.c の間に共有ヘッダーはありませんが、 bar.c はリンクされるときに foo.c で宣言された何かが必​​要であり、 foo.c はリンクされるときに bar.c からの関数が必要です。 /P>

「extern」を使用することで、コンパイラに、その後に続くものはすべてリンク時に検出される (非静的) ことを伝えます。後で遭遇するので、現在のパスでは何も予約しないでください。この点で、関数と変数は同等に扱われます。

モジュール間でグローバルを共有する必要があり、ヘッダーに配置/初期化したくない場合に非常に便利です。

技術的には、ライブラリのパブリック ヘッダー内のすべての関数は「extern」ですが、コンパイラによっては、そのようにラベル付けしてもほとんどまたはまったくメリットがありません。ほとんどのコンパイラは、自分でそれを理解できます。ご覧のとおり、これらの関数は実際には別の場所で定義されています。

上記の例では、main() は hello world を 1 回だけ出力しますが、引き続き bar_function() に入ります。また、この例では bar_function() が返されないことに注意してください (単純な例であるため)。これが十分に実用的ではないように思われる場合は、シグナルが処理されたときに stop_now が変更されることを想像してみてください (したがって、揮発性)。

Externs は、シグナル ハンドラー、ヘッダーや構造体に配置したくないミューテックスなどに非常に役立ちます。ほとんどのコンパイラは、外部オブジェクト用にメモリを予約しないように最適化します。オブジェクトが定義されているモジュールで予約します。ただし、パブリック関数のプロトタイプを作成するときに、最新のコンパイラで指定してもほとんど意味がありません。

お役に立てば幸いです:)


私が標準を覚えている限り、すべての関数宣言はデフォルトで「extern」と見なされるため、明示的に指定する必要はありません。

このキーワードは変数でも使用できるため、役に立たないわけではありません (その場合、リンケージの問題を解決する唯一の解決策です)。ただし、関数については - はい、オプションです。


関数定義とシンボル宣言という 2 つの別個の概念を区別する必要があります。 「extern」はリンケージ修飾子であり、後で参照されるシンボルが定義されている場所に関するコンパイラへのヒントです (ヒントは「ここではありません」)。

と書くと
extern int i;

C ファイルのファイル スコープ (関数ブロックの外側) では、「変数は別の場所で定義されている可能性があります」と言っています。

extern int f() {return 0;}

は関数 f の宣言であり、関数 f の定義でもあります。この場合の定義は extern をオーバーライドします。

extern int f();
int f() {return 0;}

最初に宣言があり、その後に定義が続きます。

extern の使用 ファイルスコープ変数を宣言し、同時に定義したい場合は間違っています。たとえば、

extern int i = 4;

コンパイラによっては、エラーまたは警告が表示されます。

extern の使い方 明示的に変数の定義を避けたい場合に便利です.

説明しましょう:

ファイル a.c に以下が含まれているとしましょう:

#include "a.h"

int i = 2;

int f() { i++; return i;}

ファイル a.h には以下が含まれます:

extern int i;
int f(void);

ファイル b.c には以下が含まれます:

#include <stdio.h>
#include "a.h"

int main(void){
    printf("%d\n", f());
    return 0;
}

ヘッダーの extern は、リンク段階でコンパイラに「これは宣言であり、定義ではない」と伝えるため、便利です。 a.c で i を定義し、それにスペースを割り当て、値を割り当てる行を削除すると、プログラムは未定義の参照でコンパイルに失敗するはずです。これは、変数を参照したが、まだ定義していないことを開発者に伝えます。一方、「extern」キーワードを省略し、int i = 2 を削除するとします。 行、プログラムは引き続きコンパイルされます - i はデフォルト値 0 で定義されます。

ファイル スコープ変数は、関数の先頭で宣言するブロック スコープ変数とは異なり、値を明示的に割り当てない場合、既定値の 0 または NULL で暗黙的に定義されます。 extern キーワードは、この暗黙の定義を回避するため、間違いを避けるのに役立ちます。

関数の場合、関数宣言では、キーワードは確かに冗長です。関数宣言には暗黙の定義はありません。