苦しんで覚えるC言語

分割の定石


変数の共有


前節では、最低限の構成でプログラムを複数ファイルに分割しました。
しかし、共有できたのは関数だけであり、変数の共有は行いませんでした。

複数のソースファイルに分けて開発を行う場合は、
関数だけでなく、変数や定数なども共有する必要が生じてきますが、
前章の方法では、変数を共有することは出来ません。

例えば、次のようにヘッダーファイル内で変数を宣言すると、
宣言が重複している、という意味のエラーが表示され、コンパイル出来ません。

/* 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 <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;
}
このプログラムの実行結果は次の通りになります。

100
なお、sum関数の中身がそのままなのは、修正が面倒だったと言うだけです。
必要最低限に
変数の共有は大変便利なテクニックですが、あまり乱用しないで下さい。
本来、複数のファイルに分割するのは、機能毎に独立させるためです。
しかし、変数の共有を使用すると、同じ変数が使えるようになってしまい、
機能毎に独立させる意味合いが薄れてしまいます。

従って、可能な限り関数の引数や戻り値を利用し、
変数の共有は、どうしても必要な場合にのみ使用して下さい。

目次に戻る

ヘッダーファイルの重複防ぎ


ここまでは、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
説明コメントの場所
この様に、関数を説明するコメントをつけるプログラムは良く見かけます。
こうすれば、他人が見た時や、時間がたってから自分が見た時でも、
内容を素早く把握出来て便利です。

ただ、筆者は、この様なコメントはヘッダーファイルの中に書くべきで、
ソースファイルの中に書くべきではないと思います。
ヘッダーファイルはその関数を利用する全ての人が読みますが、
ソースファイルの方は誰もが読むとは限らないためです。
自動でできそうな気もするが・・・
ヘッダーファイルは書き方が決まり切っているため、
ソースファイルから自動的に生成することができるような気もします。
実際、他の多くの言語では、ヘッダーファイルに相当する宣言を自動生成します。
(Javaはクラスファイルから自動で宣言を作ります。)

しかし、ヘッダーファイルにはソースファイルの設計書という意味もあります。
先にヘッダーファイルを作り、それに合わせてプログラムを作っていくわけです。
また、ソースファイルには、ヘッダーファイルに書く必要のない、
そのソースファイル固有の関数や変数が使われていることも良くあるため、
自動生成で不要な宣言までヘッダーにしてしまうと、ある種のムダが出てしまいます。

C言語は可能な限りムダを減らせるように設計されており、
プログラマーが意識してしなければならないことが多いかわりに、
意識して行えば、無駄を大きく減らせるようになっています。

目次に戻る