ヘッダファイル
概要
ヘッダファイルは関数のプロトタイプ宣言や構造体、
定数などがまとめられているファイルのことです。
このファイルを使用すると、宣言されている関数や構造体等を
複数のソースファイルで使用できるようになります。
ヘッダファイルの種類
ヘッダファイルは2種類あります。
一つはC/C++言語で用意された関数を使用するためのヘッダファイル、
もう一つは開発側(自分たち)で用意したヘッダファイルです。
役割はどちらも同じですが、読み込むための書式が異なります。
標準ライブラリ関数用のヘッダファイルの書式
#include <ヘッダファイル名.h>
開発側で作成したヘッダファイルの書式
#include "ヘッダファイル名.h"
■ヘッダファイルの作成方法と中身
テキストファイルの拡張子を.hにすれば、ヘッダファイルとして扱われます。
ヘッダファイルの中身は、「■ヘッダファイル」の箇所で書いていますが、
関数のプロトタイプ宣言や、構造体、定数などがまとめられています。
ヘッダファイルの例
// test_01.h ///////////////////////////////////////////
void test_func(void);
// test.h ここまで /////////////////////////////////////
// test_01.cpp /////////////////////////////////////////
#include <stdio.h>
#include "test_01.h"
void test_func(void)
{
printf("test_func Run\n");
}
// test_01.cpp ここまで ////////////////////////////////
// main.cpp ////////////////////////////////////////////
#include "test_01.h"
int main(void)
{
// test_01.hのtest_funcを実行する
test_func();
return 0;
}
// test.cpp ここまで////////////////////////////////////
実行結果:
test_func Run
各ソースで行っている内容は以下の通りです。
test.h
・test_func関数のプロトタイプ宣言を行っている
test_01.cpp
・stdio.hのインクルードファイルを読み込む
・test_01.hのインクルードファイルを読み込む
・test_func関数を定義する
main.cpp
・test_01.hのインクルードファイルを読み込む
・main関数を定義する
・main関数内でtest_func関数を呼び出す
プリプロセッサで行われるヘッダの挙動
プリプロセッサではヘッダファイルの中身をcppファイルの中に展開しています。
test_01.cppをプリプロセッサで展開した内容を以下に書いてみたいと思います。
展開前のtest_01.cppファイル
#include <stdio.h>
#include "test_01.h"
void test_func(void)
{
printf("test_func Run\n");
}
展開後のtest_01.cppファイル
// <stdio.h>の中身を書くと大変なので省略
void test_func(void);
void test_func(void)
{
printf("test_func Run\n");
}
展開前と展開後のソースで変更されている箇所は
#include "test_01.h"の部分がtest_01.hファイルの中身に置換されています。
このような展開がヘッダファイルが宣言されているファイル全てで行われています
多重定義エラー
多重定義エラーとは同じ名前の構造体や変数を複数回定義した場合に発生するエラーです。
複数のインクルードファイルを読み込んだ場合に発生することがあります。
多重定義エラーが発生するソース(展開前)
// StudentData.h //////////////////////////////////////
// 学生データ
typedef struct {
char m_Name[16];
int m_TotalScore; // 合計点
} StudentData;
// StudentData.h ここまで/////////////////////////////
// JrHighSchool.h ////////////////////////////////////
#include "StudentData.h"
// 中学校データ
typedef struct
{
// 3学年、5クラス、30人分
StudentData m_StudentData[3][5][30];
} JrHighSchoolData;
// JrHighSchool.h ここまで //////////////////////////
// HighSchool.h /////////////////////////////////////
#include "StudentData.h"
// 高校データ
typedef struct {
// 3学年、8クラス、30人分
StudentData m_StudentData[3][8][30];
} HighSchoolData;
// HighSchool.h ここまで ////////////////////////////
// main.cpp /////////////////////////////////////////
#include "JrHighSchool.h"
#include "HighSchool.h"
int main(void)
{
JrHighSchoolData jrhigh;
HighSchoolData high;
return 0;
}
// main.cpp ここまで ////////////////////////////////
StudentData.hをJrHighSchool.hとHighSchool.hの両方で読み込んでいます。
そしてその2つのヘッダファイルをmain.cppで読み込んでおり、
これが原因でStudentData構造体が複数回定義され多重定義エラーになっています。
多重定義エラーが発生するソース(展開後)
// main.cpp
// JrHighSchool.h 展開開始 ////////////////////////////////
// StudentData.h 展開開始 /////////////////////////////////
// 学生データ
typedef struct
{
char m_Name[16];
int m_TotalScore; // 合計点
} StudentData;
// StudentData.h 展開終了 /////////////////////////////////
// 中学校データ
typedef struct
{
// 3学年、5クラス、30人分
StudentData m_StudentData[3][5][30];
} JrHighSchoolData
// JrHighSchool.h 展開終了 ////////////////////////////////
// HighSchool.h 展開開始 ////////////////////////////////
// StudentData.h 展開開始 /////////////////////////////////
// 学生データ
typedef struct
{
char m_Name[16];
int m_TotalScore; // 合計点
} StudentData;
// StudentData.h 展開終了 /////////////////////////////////
// 高校データ
typedef struct
{
// 3学年、8クラス、30人分
StudentData m_StudentData[3][8][30];
} HighSchoolData;
// HighSchool.h 展開終了 //////////////////////////////////
int main(void)
{
JrHighSchoolData jrhigh;
HighSchoolData high;
return 0;
}
このようにmain.cppを展開した場合、StudentData構造体を
2度定義しているのでエラーになります。
多重定義エラーの回避法
多重定義エラーの回避方法はcppファイルの展開時に1度展開した
ヘッダファイルを無視することです。
回避方法には#ifndef、#define、#endifのマクロを使用します。
以下は#ifndef等を追加したソースコードです。
// StudentData.h ////////////////////////////
#ifndef STUDENT_H_
#define STUDENT_H_
// 学生データ
typedef struct
{
char m_Name[16];
int m_TotalScore; // 合計点
} StudentData;
#endif
// StudentData.h ここまで//////////////////////
// JrHighSchool.h /////////////////////////////
#ifndef JRHIGHSCHOOL_H_
#define JRHIGHSCHOOL_H_
#include "StudentData.h"
// 中学校データ
typedef struct
{
// 3学年、5クラス、30人分
StudentData m_StudentData[3][5][30];
}JrHighSchoolData;
#endif
// JrHighSchool.h ここまで/////////////////////
// HighSchool.h ///////////////////////////////
#ifndef HIGHSCHOOL_H_
#define HIGHSCHOOL_H_
#include "StudentData.h"
// 高校データ
typedef struct
{
// 3学年、8クラス、30人分
StudentData m_StudentData[3][8][30];
} HighSchoolData;
#endif
// HighSchool.h ここまで/////////////////////////
// main.cpp
#include "JrHighSchool.h"
#include "HighSchool.h"
int main(void)
{
JrHighSchoolData jrhigh;
HighSchoolData high;
return 0;
}