前節では、最低限の構成でプログラムを複数ファイルに分割しました。
しかし、共有できたのは関数だけであり、変数の共有は行いませんでした。
複数のソースファイルに分けて開発を行う場合は、
関数だけでなく、変数や定数なども共有する必要が生じてきますが、
前章の方法では、変数を共有することは出来ません。
例えば、次のようにヘッダーファイル内で変数を宣言すると、
宣言が重複している、という意味のエラーが表示され、コンパイル出来ません。
このエラーをより正確に理解するには、宣言の意味を理解する必要があります。/* sum.h */ int sum(int min,int max); int Public;
変数や関数の宣言を行うと、コンパイラがその名前と形を記憶します。
これが、宣言と呼ばれる機能です。
そして、同時に、コンパイラは実際に変数や関数を作成します。
これが、定義と呼ばれる機能です。
これまでの変数では、この宣言と定義を常に同時に行っていたのです。
宣言は、変数や関数の形をコンパイラに教えるだけなので、
その形さえ同じであれば、何回宣言しても問題ありません。
しかし、定義では、関数や変数の実体を作成することになります。
同じ関数や変数が何回も作られると区別がつかなくなるためエラーとなります。
[ プロトタイプ宣言なら ]
前章では、プロトタイプ宣言だけを記述して成功しましたが、
これは、プロトタイプ宣言は、宣言だけを行い、定義は行わないからです。
従って、プロトタイプ宣言は(同じ書き方なら)いくつでも書けます。
前項では、変数は宣言と定義を同時に行うため、複数回宣言できないことを説明しました。
この問題を解決するには、宣言だけを複数回行い、定義は1回で済ませる必要があります。
その場合、宣言だけを行うextern(エクスターン)宣言が用意されています。
extern宣言の使い方は簡単です。ただ、今までの宣言の前に extern と記述するだけです。
[ extern宣言 ]
externを使用して宣言だけを行い定義は行わない宣言方法。
/* sum.h */ extern int sum(int min,int max); extern int Public;
このextern宣言を使うと、異なるソースファイルで変数を共有することが出来ます。
まずは、ヘッダーファイル内でなんらかの変数をextern宣言します。
これで、変数Publicは sum.h をインクルードしている全てのソースファイルで共有出来ます。/* sum.h */ extern int sum(int min,int max); extern int Public; /* 変数のextern宣言 */
これで、変数Publicは main.c からも sum.c からも使えるようになります。/* sum.c */ int Public; /* 変数の実体の作成 */ int sum(int min,int max) { int num; num = (min + max) * (max - min + 1) / 2; return num; }
/* main.h */ #include <stdio.h> #include "sum.h" int main(void) { int value; value = sum(50,100); printf("%d\n",Public); return 0; }
このプログラムの実行結果は次の通りになります。/* sum.c */ int Public; int sum(int min,int max) { int num; num = (min + max) * (max - min + 1) / 2; Public = 100; return num; }
なお、sum関数の中身がそのままなのは、修正が面倒だったと言うだけです。
100
[ 必要最低限に ]
変数の共有は大変便利なテクニックですが、あまり乱用しないで下さい。
本来、複数のファイルに分割するのは、機能毎に独立させるためです。
しかし、変数の共有を使用すると、同じ変数が使えるようになってしまい、
機能毎に独立させる意味合いが薄れてしまいます。
従って、可能な限り関数の引数や戻り値を利用し、
変数の共有は、どうしても必要な場合にのみ使用して下さい。
ここまでは、extern宣言を使用して、重複して定義されることを回避してきましたが、
実は、ヘッダーファイルの重複インクルードを防ぐ方法もあります。
それには、#ifndef〜#endif疑似命令を使用します。
#ifndef〜#endif疑似命令は、ある記号が定義されていなかった場合だけ、
その間に挟まれたプログラムをコンパイルするという記号です。
この性質を利用して、次のようなヘッダーファイルを作成出来ます。
このヘッダーファイルでは、まず最初に、記号_INCLUDE_SUM_が定義されているか調べて、/* sum.h */ #ifndef _INCLUDE_SUM_ #define _INCLUDE_SUM_ int sum(int min,int max); #endif
この様にすれば、同じ宣言を何度も行うことがなくなります。
2回目以降はコンパイルされないのでは、1つしか使えなくなるようにも思えますが、
最終的には全てのソースファイルは結合されるので、1回コンパイルすれば十分です。
なお、一般には、extern宣言も組み合わせて、次のようにします。
更に、この様なコメントを入れると、より良いヘッダーファイルが完成します。
/* sum.h */ #ifndef _INCLUDE_SUM_ #define _INCLUDE_SUM_ /* min〜max間の合計値を計算する関数 int min 最小値 int max 最大値 戻り値 int 合計値 */ extern int sum(int min,int max); #endif
[ 説明コメントの場所 ]
この様に、関数を説明するコメントをつけるプログラムは良く見かけます。
こうすれば、他人が見た時や、時間がたってから自分が見た時でも、
内容を素早く把握出来て便利です。
ただ、筆者は、この様なコメントはヘッダーファイルの中に書くべきで、
ソースファイルの中に書くべきではないと思います。
ヘッダーファイルはその関数を利用する全ての人が読みますが、
ソースファイルの方は誰もが読むとは限らないためです。
[ 自動でできそうな気もするが・・・ ]
ヘッダーファイルは書き方が決まり切っているため、
ソースファイルから自動的に生成することができるような気もします。
実際、他の多くの言語では、ヘッダーファイルに相当する宣言を自動生成します。
(Javaはクラスファイルから自動で宣言を作ります。)
しかし、ヘッダーファイルにはソースファイルの設計書という意味もあります。
先にヘッダーファイルを作り、それに合わせてプログラムを作っていくわけです。
また、ソースファイルには、ヘッダーファイルに書く必要のない、
そのソースファイル固有の関数や変数が使われていることも良くあるため、
自動生成で不要な宣言までヘッダーにしてしまうと不都合もあります。
こんな理由からC言語ではヘッダーファイルを手動で作成します。
なお、ソースファイルに追加のキーワードを埋め込んでやることで、
ヘッダーファイルの自動生成が可能になります。
興味のある人は挑戦してみてはいかがでしょうか。