他の答えは正しいですが、静的関数が別のファイルからアクセスできないと言うのは正確ではありません。関数ポインタを介して関数にアクセスすることが可能です。関数の名前は、別の翻訳単位ではアクセスできないと言った方が正確です。
Cソースコードを実行可能プログラムに変換することは、次のような概念的な段階で構成されていることに注意してください。
- 前処理(
#include
ディレクティブがインクルードされたファイルの内容に置き換えられる)
- コンパイル(一度に1つの翻訳単位を処理します)
- リンク(翻訳ユニットが最終的なプログラムにまとめられる)
3つのファイルがあるとします。 foo.h
:
typedef void (*void_function_p)(void);
extern void_function_p foo(void);
foo.c
:
#include "foo.h"
#include <stdio.h>
static void baz(void) {
printf("worked!\n");
}
void_function_p foo(void) {
return baz;
}
bar.c
:
#include "foo.h"
#include <stdio.h>
int main(void) {
(*foo())();
return 0;
}
このプログラムは、「動作しました」をコンパイルして出力します。それが実行されるとき。
ここには2つの翻訳ユニットがあります。1つは、前処理されたコードですfoo.c
(これは、動作方法のために、および#include
のコードも含まれます)。もう1つは、前処理されたコードです(これも、とにコードの独自のコピーがあります)。foo.h
stdio.h
bar.c
foo.h
stdio.h
関数foo
に静的関数へのポインタを返すようにすることで、関数baz
から呼び出すことができます。baz
main
ここで、次のように変更するとどうなるかを考えてmain
みましょう。
int main(void) {
(*foo())();
baz();
return 0;
}
baz
このコードは、この翻訳ユニットの名前を他の翻訳ユニットの定義にリンクできないため、リンカーエラーが発生しますbaz
。
これが静的関数の最初の利点です。別のプログラマーがbaz
別の変換ユニットから誤って関数にアクセスすることはありません。
ここで、次のように変更するとどうなるかを考えてbar.c
みましょう。
#include "foo.h"
#include <stdio.h>
static void baz(void) {
printf("still works!");
}
int main() {
(*foo())();
baz();
return 0;
}
このコードはコンパイルされ、「動作しました」と出力されます。続いて「まだ動作します!」
これは、静的関数の2番目の利点です。同じ名前の2つの関数を(異なる変換単位で)定義しました。
両方の静的定義を同じ変換単位に入れようとすると、baz
2回定義することについてコンパイラエラーが発生します。
最後に、プログラムを現在の状態のままにしてすべてのを削除すると、(外部リンケージを使用して)2回定義されているstatic
ため、リンカーエラーが発生します。これは許可されていません。baz