今まで学んできたC言語のプログラムは、1つのファイルの中にすべてのプログラムを記述してきました。単純で小さなプログラムならそれでもよいのですが、大きなプログラムになると、 何十万、何百万行という膨大な 量となり、見にくくなります。そこで、機能や、役割毎など、ソースコードを分割してプログラムを書くことで、わかりやすくなります。
ソースファイルとヘッダーファイル
printf関数などを使用するときに、#include <stdio.h>を先頭に記述していたと思います。「C言語プログラミングの基本と書き方について」のところで、stdio.hというファイルを#includeと書かれている行にペーストするということを説明させていただきましたが、実は、これがファイルに分割するということを示していました。
ヘッダーファイルは、関数のプロトタイプ宣言や、新しいデータ型の定義、定数定義などが記載されたファイルです。
基本的にヘッダーファイルはいろんな定義が書かれているのみで、処理は記述しません。
ヘッダーファイルは、拡張子として、.hを付けることが慣習となっています。
実際に処理を記述するのは、ソースファイルのソースファイルになります。
main関数から呼び出される関数を別のファイルに記述します。
avrg.cファイルを作り、ソースコードは次のように記述します。
avrg.c
double avrg(int num1, int num2)
{
double ret;
ret = ( num1 + num2 ) / 2.0;
return ret;
}
次に、avrg.cのプロトタイプ宣言をavrg.hファイルに記述します。
avrg.h
double avrg(int num1, int num2);
そして、main.cファイルにmain関数を記述します。
main.c
#include <stdio.h>
#include "avrg.h"
int main(void)
{
double val;
val = avrg(10, 20);
printf("%lf\n", val);
return 0;
}
2行目で、avrg.hをインクルードしているのですが、" "で囲んでいます。これは、自作したヘッダーファイルをインクルードするときは、" " で囲むことになっているためです。
複数ファイルをコンパイルする場合は、それぞれのソースファイルを記述してください。
gccコンパイラの場合は、次のようになります。
gcc main.c avrg.c
実行結果は次のようになります。
実行結果
15.000000
このように、avrg.hをインクルードするだけで、avrg.cに記述した関数を利用できるようになります。
extern宣言の意味
よく、ヘッダーファイルの関数のプロトタイプ宣言の前にexternが記載されているのをよく見かけます。
extern宣言とは、宣言だけを行い定義は他のファイルで行うことを示すために付けられます。ただし、関数のプロトタイプ宣言については、externを明示していなくても、すべてのソースコードのどこかで定義していると決まっているため、付けても付けなくても同じ意味となります。ただ、慣習的に付けることが多いため、ここで紹介しておきます。
では、externを付けないといけない場合は、どんなときでしょうか?
それは、グローバル変数の宣言を行う場合です。グローバル変数とは、関数の外で宣言した変数のことを言います。また、関数内で宣言した変数のことをローカル変数を言います。
このグローバル変数は、他のファイルでも使用することができます。他のファイルで使用する場合は、ヘッダーファイルに変数を宣言する必要があります。
ただし、変数は、関数のように宣言と定義を分けることができません。ソースファイルに定義した変数をヘッダーファイルに記述すると、同じ変数名が定義されているため、エラーとなってしまいます。
そこで、ヘッダーファイルの変数の宣言ではextern を付けることで、この変数は、宣言のみで、定義は別のファイルで行っていることとなり、エラーとはならなくなります。
プロトタイプ宣言も、変数の宣言も、別ファイルに定義がある場合は、externを付けるようにしてください
ヘッダーファイルの重複を防ぐ
ヘッダーファイルを作成するときに、ヘッダーファイルのインクルードの重複を防ぐ記述がよく行われますので、ここで紹介します。
インクルードの重複防止では、#ifndef~#endif疑似命令を使用します。
書き方は、ヘッダーファイルの最初に次のように記述します。
#ifndef _AVRG_H_
#define _AVRG_H_
double avrg(int num1, int num2);
#endif
1行目の#ifndefは、右側に記述するマクロ定義がされていない場合は、#endifまでの処理をそのままコンパイル対象にするという記述になります。
サンプルコードでは、もし、_AVRG_H_というマクロ定義がされていない場合は、#endifまでの処理をコンパイルします。
2行目で_AVRG_H_を定義します。次にこのヘッダーファイルがどこかのファイルでインクルードされている場合、マクロ定義済みとなっているため、1行目の#ifndefでは、定義済みのため、コンパイルは行われません。
このように、同じ宣言を何度も行うことがなくなります。
他のファイルで使用したいのに1回しか使用できないと思うかもしれませんが、最終的にソースファイルはまとめられて1つになるため、1回コンパイルできれば使用できますので、問題ありません。
以上が、ソースコードを複数のファイルに分割するために必要最低限のことについて学びました。
これで、一通りC言語の基本的なことは学べました。ただ、もっと、いろんな使い方や、こんなケースの場合はどうしたらよいかなど、引っかかることは大いにあると思いますが、以上のことが学べたら実際にプログラムを作りながら、ここで学ばなかったことも紹介したいと思います。
また、C言語入門は常に見直しを行っていきますので、わかりにくいと感じた個所がありましたら、改善しますので、コメント、問い合わせいただけると助かります。