分割の定石
変数の共有
前節では、最低限の構成でプログラムを複数ファイルに分割しました。
しかし、共有できたのは関数だけであり、変数の共有は行いませんでした。
複数のソースファイルに分けて開発を行う場合は、
関数だけでなく、変数や定数なども共有する必要が生じてきますが、
前章の方法では、変数を共有することはできません。
たとえば、次のようにヘッダーファイル内で変数を宣言すると、
宣言が重複している、という意味のエラーが表示され、コンパイルできません。
/* sum.h */
int sum(int min, int max);
int Public;
このエラーをより正確に理解するには、
宣言の意味を理解 する必要があります。
これまで、関数にしろ変数にしろ、宣言する、と表現してきましたが、
実は、宣言には2種類の機能があります。
変数や関数の宣言を行うと、コンパイラがその名前と形を記憶します。
これが、
宣言 と呼ばれる機能です。
そして、同時に、コンパイラは実際に変数や関数を作成します。
これが、
定義 と呼ばれる機能です。
これまでの変数では、この宣言と定義を常に同時に行っていたのです。
宣言は、変数や関数の形をコンパイラに教えるだけなので、
その形さえ同じであれば、何回宣言しても問題ありません。
しかし、定義では、関数や変数の実体を作成することになります。
同じ関数や変数が何回も作られると区別がつかなくなるためエラーとなります。
前章では、プロトタイプ宣言だけを記述して成功しましたが、 これは、プロトタイプ宣言は、宣言だけを行い、定義は行わないからです。 したがって、プロトタイプ宣言は(同じ書き方なら)いくつでも書けます。
extern宣言
前項では、変数は宣言と定義を同時に行うため、複数回宣言できないことを説明しました。
この問題を解決するには、宣言だけを複数回行い、定義は1回で済ませる必要があります。
その場合、宣言だけを行う
extern(エクスターン)宣言 が用意されています。
【extern宣言】
宣言だけを行い定義は行わない宣言方法。
extern宣言の使い方は簡単です。ただ、今までの宣言の前に extern と記述するだけです。
次のヘッダーファイルは、関数と変数に対してextern宣言を行っています。
/* sum.h */
extern int sum(int min, int max);
extern int Public;
このextern宣言を使うと、
異なるソースファイルで変数を共有する ことができます。
まずは、ヘッダーファイル内でなんらかの変数をextern宣言します。
/* sum.h */
extern int sum(int min, int max);
extern int Public; /* 変数のextern宣言 */
これで、変数Publicは sum.h をインクルードしているすべてのソースファイルで共有できます。
しかし、これだけでは定義がされていないため、変数Publicは作られていません。
そこで、どこか1つのソースファイルの中で普通の宣言を行って実体を作成します。
/* sum.c */
int Public; /* 変数の実体の作成 */
int sum(int min, int max)
{
int num;
num = (min + max) * (max - min + 1) / 2;
return num;
}
これで、変数Publicは main.c からも sum.c からも使えるようになります。
次のプログラムは、それを実際に試してみた例です。
/* main.c */
#include "sum.h"
#include <stdio.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;
}
このプログラムの実行結果は次の通りになります。
変数の共有は大変便利なテクニックですが、あまり乱用しない でください。 本来、複数のファイルに分割するのは、機能毎に独立させるためです。 しかし、変数の共有を使用すると、同じ変数が使えるようになってしまい、 機能毎に独立させる意味合いが薄れてしまいます。 したがって、可能な限り関数の引数や戻り値を利用し、 変数の共有は、どうしても必要な場合にのみ使用してください。
ヘッダーファイルの重複防ぎ
ここまでは、extern宣言を使用して、重複して定義されることを回避してきましたが、
実は、
ヘッダーファイルの重複インクルードそれ自体を防ぐ 方法もあります。
それには、
#ifndef~#endif疑似命令 を使用します。
#ifndef~#endif疑似命令は、ある記号が定義されていなかった場合だけ、
その間に挟まれたプログラムをコンパイルするという記号です。
この性質を利用して、次のようなヘッダーファイルを作成できます。
/* sum.h */
#ifndef _INCLUDE_SUM_
#define _INCLUDE_SUM_
int sum(int min, int max);
#endif
このヘッダーファイルでは、最初に、記号_INCLUDE_SUM_が定義されているか調べて、
定義されていなかった場合だけ、その後のプログラムをコンパイルします。
ここでは、後にコンパイルされるプログラムの中で、#define疑似命令を使って、
記号_INCLUDE_SUM_ を定義しているので、このヘッダーファイルが2回目に呼び出された場合、
記号_INCLUDE_SUM_ が
すでに定義されている ことになり、コンパイルは行われません。
この様にすれば、同じ宣言を何度も行うことがなくなります。
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
この様に、関数を説明するコメントをつけるプログラムは良く見かけます。 こうすれば、他人が見た時や、時間がたってから自分が見た時でも、 内容を素早く把握できて便利です。 ただ、筆者は、この様なコメントはヘッダーファイルの中に書く べきで、 ソースファイルの中に書くべきではないと思います。 ヘッダーファイルはその関数を利用するすべての人が読みますが、 ソースファイルの方は誰もが読むとは限らないためです。
ヘッダーファイルは書き方が決まり切っているため、 ソースファイルから自動的に生成することができるような気もします。 実際、他の多くの言語では、自動でやってくれるため、ヘッダーファイルは不要です。 しかし、ヘッダーファイルにはソースファイルの設計書 という意味もあります。 先にヘッダーファイルを作り、それに合わせてプログラムを作っていくわけです。 また、ソースファイルには、ヘッダーファイルに書く必要のない、 そのソースファイル固有の関数や変数が使われていることも良くあるため、 自動生成で不要な宣言までヘッダーにしてしまうと、ある種のムダが出てしまいます。 C言語はプログラマーが意識しなければならないことが多いかわりに、 意識して行えば、ムダを大きく減らせるようになっています。
前のページ
次のページ