第13問の答え

答え

モジュール内でのみ使用する変数がグローバルになっている!
double sumOfValues = 0;
int numberOfValues = 0;
グローバルにする必要のない変数はスコープを限定したほうがベター!
static double sumOfValues = 0;
static int numberOfValues = 0;

解説

今回のプログラム(ソースファイル「average.c」)は、2つの関数をインターフェースとするモジュールでした。つまり、以下のような2つの宣言(プロトタイプ宣言)にもとづいて、ほかのモジュールから呼び出されるということです。

void AddValue(double n);
void PrintAverage(void);

「average.c」にある、これらの宣言以外のすべてのものは、モジュールの内部的な実装です。具体的にどのような実装になっているかは、呼び出す側が知る必要はありません。

標準関数が使える理由も、これと同じなのよ。
え?どういうことですか?
標準関数を呼び出すには、宣言が必要よね?
はい。使いたい関数の宣言をインクルードする必要があるってことですね。
でも、関数の中身がどうなっているかは気にしないでしょう?
たしかに!実装を知る必要がないって、そういうことなんですね。

呼び出し側が知る必要のない実装は、呼び出し側から見えないようにしておくほうがベターです。これは、設計をするうえでの基本となる、「情報隠蔽」という考え方です。

ところが、問題のプログラムでは、以下の変数のスコープがグローバルになっていました。

double sumOfValues = 0;
int numberOfValues = 0;

これら2つの変数は「average.c」のファイル内でしか使わないものですが、グローバルのままではどこからでもアクセスできてしまいます。staticを付けて、「ファイルスコープ」にしてしまいましょう。

static double sumOfValues = 0;
static int numberOfValues = 0;

こうしておけば、モジュールの外部からこれら2つの変数に直接アクセスすることはできなくなります。

外部からアクセスできなくなると、何が嬉しいんですか?
いい質問ね!それはね、あとで実装を変えたくなったときのことを考えてみて。
もっといい実装方法を思い付いたときとかですか?
そうそう。そのときに、余計なものがグローバルになっていると……
うーん、なんだか手を出しにくい感じがしますね。
ほかのモジュールがアクセスしている可能性があるからね。
なるほど!中身を改良したいだけなのに、外部に影響が出てしまうってことですね。
そういうこと!ソースファイルが2つ以上になったら気を配りたいポイントね。
はーい。分かりました!

グローバル変数やグローバル関数は、そのモジュールのインターフェースとなるものだけにしましょう。そうすれば、あとからモジュール内部の実装を改良したくなったとき、その影響を最小限に抑えられます。プログラムの規模が大きくなってモジュールの数が増えていくほど、このようにスコープに気を配ることが重要になっていきます。

ここがポイント!
変数や関数をグローバルにするのは、モジュールの外部から使うときだけにしよう!

修正後のプログラム

average.c
#include <stdio.h>

static double sumOfValues = 0;
static int numberOfValues = 0;

void AddValue(double n) {
  sumOfValues += n;
  numberOfValues += 1;
}

void PrintAverage(void) {
  if (numberOfValues > 0) {
    printf("Average = %f\n", sumOfValues / numberOfValues);
  } else {
    printf("No data.\n");
  }
}