答え
#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) {
……
新しくヘッダーファイルを作ったら、何回でもインクルードできるようにしておきましょう。それには次のようにします。
#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重インクルードを「ガード」するテクニックは、プログラムが大きくなるほど重要になっていきます。例えば、問題のプログラムに「三角柱の体積」を求めるモジュールを追加することを考えてみましょう。このモジュールのヘッダーファイルからは、おそらく「triangle.h」をインクルードする必要があるでしょう。
さらに「三角錐の体積」を求めるモジュールも追加したらどうなるでしょうか。こちらのヘッダーファイルからも、「triangle.h」がインクルードされますね。
つまり、こういう状況になります。
- 「三角柱」は「triangle.h」をインクルードする
- 「三角錐」は「triangle.h」をインクルードする
この状況で「三角柱」と「三角錐」のヘッダーファイルを両方ともインクルードしたら、「triangle.h」は間接的に2回インクルードされることになります。これが、2重インクルードの「ガード」が重要な理由です。
修正後のプログラム
#ifndef triangle_h
#define triangle_h
typedef struct {
double base;
double height;
} Triangle;
double TriangleArea(const Triangle *triangle);
#endif /* triangle_h */
#include "triangle.h"
double TriangleArea(const Triangle *triangle) {
return triangle->base * triangle->height / 2.0;
}
#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