今回は、グローバルな変数や関数について、staticとexternの2つの違いや仕組みを説明していきます。
extern宣言とは何か?
まずはexternについて解説をしていきたいのですが、その前にグローバル変数を知っておく必要があります。
グローバル変数とは?
※グローバル変数について分かっている人は飛ばしてください。
グローバル変数を定義をすると、その変数はそのファイル内のどこからでもアクセスすることが可能になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include<stdio.h> void sub_func(void); /* グローバル変数 */ int gloval_value; int main(void) { gloval_value = gloval_value + 1; printf("main():gloval_value...%d\n",gloval_value); sub_func(); return 0; } void sub_func(void) { gloval_value = gloval_value + 10; printf("sub_func():gloval_value...%d\n",gloval_value); } |
1 2 |
main():gloval_value...1 sub_func():gloval_value...11 |
上記のように、関数外にグローバル変数(gloval_value)を宣言することで、main関数内でもsub_func関数内でも参照することが出来ます。
複数の関数で同じ値を共有したい場合は、グローバル変数をすることで簡単に値を共有することが出来ます。
extern宣言をすると何が変わるか?
グローバル変数を使用していると、他のファイルの関数でも変数を参照させたい場合があります。
グローバル変数は複数の関数で共有することが出来ますが、他のファイルをまたいで共有することは出来ません。
ではどうするかと言うと、「このグローバル変数は他のファイルでも参照して良いよ!」という宣言をファイルに書く必要があります。
それがextern宣言です。
グローバル変数にexternを宣言を追加してあげることで、他のファイル間でもグローバル変数を共有することが可能になります。
いつものようにグローバル変数を片方のファイルで定義し、別ファイルでextern付きのグローバル変数を宣言すればOKです!
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include<stdio.h> /* グローバル変数 */ int gloval_value ; int main(void) { gloval_value = gloval_value + 1; printf("main.c:gloval_value...%d\n",gloval_value); sub_func(); return 0; } |
1 2 3 4 5 6 7 8 9 |
#include<stdio.h> /* グローバル変数をextern宣言 */ extern int gloval_value ; void sub_func(void) { gloval_value = gloval_value + 10; printf("sub.c:gloval_value...%d\n",gloval_value); } |
1 2 |
main.c:gloval_value...1 sub.c:gloval_value...11 |
sub.cにグローバル変数(gloval_value)をextern宣言しているため、main.cのメイン関数でも、sub.cのsub_func関数でもグローバル変数(gloval_value)を参照することが出来ます。
また、今回は例として変数をextern宣言しましたが、同様に配列や関数もexternを付けることが出来ます。
extern宣言の記載場所はヘッダーファイルに
先ほどは説明のために、sub.c内にextern宣言を書きましたが、実際にはヘッダーファイルにextern宣言を書くことが多いです。
なので以下のようにヘッダーファイルを作成し、ヘッダーファイルにextern宣言を書き、cファイル内のどれかにグローバル変数を書くようにしてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include<stdio.h> /* グローバル変数 */ int gloval_value ; int main(void) { gloval_value = gloval_value + 1; printf("main.c:gloval_value...%d\n",gloval_value); sub_func(); return 0; } |
1 2 3 4 5 6 7 8 |
#include<stdio.h> #include"main.h" void sub_func(void) { gloval_value = gloval_value + 10; printf("sub.c:gloval_value...%d\n",gloval_value); } |
1 2 |
/* グローバル変数をextern宣言 */ extern int gloval_value ; |
ちなみに上記ではmain.cにグローバル変数(gloval_value)の定義していますが、sub.cに書いても大丈夫です。
staticとは何か?
externが”外部への公開”とするなら、staticはその逆で”外部への非公開”となります。
グローバル変数や関数にstaticを付けることで、外部からは参照出来なくなります。
1 2 3 4 5 6 7 8 9 |
#include<stdio.h> int main(void) { printf("プログラムを開始します\n"); sub_func(); return 0; } |
1 2 3 4 5 6 |
#include<stdio.h> static void sub_func(void) { printf("サブ関数がコールされました\n"); } |
1 2 3 4 5 6 7 |
main.c: In function ‘main’: main.c:6:5: warning: implicit declaration of function ‘sub_func’ [-Wimplicit-function-declaration] sub_func(); ^~~~~~~~ /tmp/ccZHsQUe.o: 関数 `main' 内: main.c:(.text+0x16): `sub_func' に対する定義されていない参照です collect2: error: ld returned 1 exit status |
main.cのmain関数から、sub.cのsub_func関数をコールしようとしました。
しかし、sub_func関数の先頭にstaticが付いており、sub_func関数外部から参照出来ない関数なので、コンパイルエラーになりました。
このように、外部から参照されたくない関数や変数には、staticを付けるようにしましょう。また、明らかにそのファイル内でしか使用しない関数や変数にもstaticを付けることが多いです。
staticもexternも付けない場合どうなる?
「externを付けると外部への公開、staticを付けると外部へ非公開」と言うように説明しました。
では、「externもstaticも付けないとどんな動作になるか?」を考えていきます。
関数に何もつけなかった場合
関数にstaticやexternを付けなかった場合、どういう動作を起こすか?試しにコードを書いてコンパイルしてみます。
1 2 3 4 5 6 7 8 9 |
#include<stdio.h> int main(void) { printf("プログラムを開始します\n"); sub_func(); return 0; } |
1 2 3 4 5 6 |
#include<stdio.h> void sub_func(void) { printf("サブ関数がコールされました\n"); } |
1 2 |
プログラムを開始します サブ関数がコールされました |
ぼくの環境では、警告は出ましたが、コンパイルが通ってしまいました。
なので関数にstaticやexternを付けないと、勝手に他のファイルにアクセスされる可能性があります。
これが外部公開する関数なら良いのですが、公開しない関数だったらバグに繋がってしまいますね。。
変数に何もつけなかった場合
次にグローバル変数にstaticやexternを付けずに、同じグローバル変数を2つのファイルに用意してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include<stdio.h> /* グローバル変数 */ int gloval_value; int main(void) { gloval_value = 10; sub_func(); printf("gloval_value...%d\n",gloval_value); return 0; } |
1 2 3 4 5 6 7 8 9 |
#include<stdio.h> /* グローバル変数 */ int gloval_value; void sub_func(void) { gloval_value = gloval_value + 10; } |
1 |
gloval_value...20 |
これもコンパイルが通り、実行結果は”20″になりました。なので、sub_funcにもgloval_valueの値が参照出来ていることになります。
本当はmain.c内で使用したい変数なのに、他ファイルでたまたま同じグローバル変数名の変数があれば、勝手に他のファイルで値を書き換えれてしまうことが出来ます。
このように、staticやexternを付けないと、本来とは違う動きになってしまう可能性があります。
そのためグローバル変数や関数には、「外部公開するのか、非公開にするのか」を分かりやすくするためにも、externやstaticをきちんと付けるようにしましょう。
最後に
staticとexternはこれからも見る機会は多いと思います。知らないと、実装の時やデバッグをするときにすごく困ることになるので、おさえておきましょう!
ちなみにぼくは新人の頃、ヘッダーファイルにstatic関数のプロトタイプ宣言を書いて、見事コンパイルエラーを出し、上司に注意されました。
ヘッダーファイルにstatic関数のプロトタイプ宣言をするのがなぜ良くないのか、みなさんは分かりますか?ぜひ考えてみて下さい!