第19問の答え

答え

問題のヘッダーファイルは2回インクルードするとエラーになってしまう!
#include "triangle.h"
……
#include "triangle.h"
ヘッダーファイル内に「ガード」を入れるのが正解!
#ifndef triangle_h
#define triangle_h

……

#endif /* triangle_h */

解説

これは、「2重インクルード」に関する出題でした。問題の「triangle.h」は、うっかり次のように2回インクルードするとエラーになってしまいます。

#include <stdio.h>
#include <stdlib.h>
#include "triangle.h"
#include "triangle.h"

int main(void) {
    ……

「triangle.h」の中には次のようなtypedefがあり、これが重複してしまうためです。

typedef struct {
  double base;
  double height;
} Triangle;

このような問題は、標準関数のヘッダーファイルでは発生しません。例えば、次のように「stdio.h」を2回以上インクルードしてもエラーにはならないのです。

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include "triangle.h"

int main(void) {
    ……

新しくヘッダーファイルを作ったら、何回でもインクルードできるようにしておきましょう。それには次のようにします。

triangle.h
#ifndef triangle_h /* 2回目以降はマクロが定義済みなので #endif まで読み飛ばす */
#define triangle_h /* 1回目ならマクロを定義 */

typedef struct {
  double base;
  double height;
} Triangle;

double TriangleArea(const Triangle *triangle);

#endif /* 2回目以降はここまで読み飛ばされる */

問題の「triangle.h」に、全体を囲むような記述が追加されたのが分かるでしょうか。これは、1回目のインクルード時にだけ、「自分はすでにインクルードされた」という意味のマクロを定義するものです。ほかのヘッダーファイルとかぶらないように、ファイル名に合わせたマクロ名になっています。

2回目以降のインクルードでは、すでにマクロが定義済みなので全体がスキップされます。これならtypedefの部分も1度しか読まれないため、エラーは発生しません。

このテクニックを覚えておけば、2重インクルードを「ガード」できるのよ。
まぁ、そうですよね。そういうテクニックがあるのは分かりましたけど……
あらら。納得いかないところがあったかしら?
えっと、2重にインクルードしなければいいだけじゃないんですか?
あぁ、そこが疑問だったのね。

2重インクルードを「ガード」するテクニックは、プログラムが大きくなるほど重要になっていきます。例えば、問題のプログラムに「三角柱の体積」を求めるモジュールを追加することを考えてみましょう。このモジュールのヘッダーファイルからは、おそらく「triangle.h」をインクルードする必要があるでしょう。

さらに「三角錐の体積」を求めるモジュールも追加したらどうなるでしょうか。こちらのヘッダーファイルからも、「triangle.h」がインクルードされますね。

つまり、こういう状況になります。

  • 「三角柱」は「triangle.h」をインクルードする
  • 「三角錐」は「triangle.h」をインクルードする

この状況で「三角柱」と「三角錐」のヘッダーファイルを両方ともインクルードしたら、「triangle.h」は間接的に2回インクルードされることになります。これが、2重インクルードの「ガード」が重要な理由です。

なるほど!2重インクルードでエラーになっちゃうと、どうしても困るときがあるってことですね。
そういうこと。やり方も決まってるし、簡単でしょ?
簡単ですねー!
ここがポイント!
ヘッダーファイルは、何回インクルードされても大丈夫なように「ガード」しておこう!

修正後のプログラム

triangle.h
#ifndef triangle_h
#define triangle_h

typedef struct {
  double base;
  double height;
} Triangle;

double TriangleArea(const Triangle *triangle);

#endif /* triangle_h */
triangle.c
#include "triangle.h"

double TriangleArea(const Triangle *triangle) {
  return triangle->base * triangle->height / 2.0;
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include "triangle.h"

int main(void) {
  Triangle triangle = {
    .base = 6.0,
    .height = 4.0
  };

  printf("area = %f\n", TriangleArea(&triangle));

  return EXIT_SUCCESS;
}
実行結果
area = 12.000000