<戻る  目次  進む>  
          <戻 進> 雑誌 白黒 明暗   フォント 既定 ゴシック 明朝 手書き   サイズ   標準  


   分割の定石   

  1. 第1項:変数の共有
  2. 第2項:extern宣言
  3. 第3項:ヘッダーファイルの重複防ぎ

[1]変数の共有

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

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

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


/* sum.h */
int sum(int min,int max);
int Public;
このエラーをより正確に理解するには、宣言の意味を理解する必要があります。
これまで、関数にしろ変数にしろ、宣言する、と表現してきましたが、
実は、宣言には2種類の機能があります。

変数や関数の宣言を行うと、コンパイラがその名前と形を記憶します。
これが、宣言と呼ばれる機能です。
そして、同時に、コンパイラは実際に変数や関数を作成します。
これが、定義と呼ばれる機能です。
これまでの変数では、この宣言と定義を常に同時に行っていたのです。

宣言は、変数や関数の形をコンパイラに教えるだけなので、
その形さえ同じであれば、何回宣言しても問題ありません。
しかし、定義では、関数や変数の実体を作成することになります。
同じ関数や変数が何回も作られると区別がつかなくなるためエラーとなります。


[  プロトタイプ宣言なら  ]
前章では、プロトタイプ宣言だけを記述して成功しましたが、
これは、プロトタイプ宣言は、宣言だけを行い、定義は行わないからです。
従って、プロトタイプ宣言は(同じ書き方なら)いくつでも書けます。

目次に戻る


[2]extern宣言

前項では、変数は宣言と定義を同時に行うため、複数回宣言できないことを説明しました。
この問題を解決するには、宣言だけを複数回行い、定義は1回で済ませる必要があります。
その場合、宣言だけを行うextern(エクスターン)宣言が用意されています。

[  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関数の中身がそのままなのは、修正が面倒だったと言うだけです。

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

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

目次に戻る


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

ここまでは、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言語ではヘッダーファイルを手動で作成します。

なお、ソースファイルに追加のキーワードを埋め込んでやることで、
ヘッダーファイルの自動生成が可能になります。
興味のある人は挑戦してみてはいかがでしょうか。

目次に戻る


<−前に戻る  先頭に戻る  次へ進む−>