- ベストアンサー
ヘッダファイルの基本から作り方
非常に基本的事かもしれませんが、意外とテキスト載ってないので聞きます。ヘッダファイル(stdio.hやctype.hなど最初からあるものを除く)の基本的役目ってなんですか?そして、これからより複雑なプログラムとなるとヘッダファイルを作成しなければいけないようですが、そのヘッダファイルのつくり方(ルールなど)をおしえてください。
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
これまでの回答でほぼ出揃っているように思いますが、ある程度の規模のソフトを開発するような場合の定石のようなものを示しておきます。 # ソースファイル数が数百以上になるような規模のプロジェクトでは、 # 普通にやっていることだとは思いますが。。。 機能分割やモジュール分割するときに、内部公開用ヘッダと外部公開用ヘッダを用意する場合があります。 こうすることで、機能部外に必要以上の情報を公開する必要もなくなり、No3 buihyaku さんのおっしゃるようなことを極力避けることができるようになります。 たとえば、こんな感じ。 【HOGE 機能部】 〔HOGE_A モジュール〕 (外部公開用ヘッダ) hoge_a.h (内部公開用ヘッダ) hoge_a_i.h 〔HOGE_B モジュール〕 (外部公開用ヘッダ) hoge_b.h (内部公開用ヘッダ) hoge_b_i.h 【PIYO 機能部】 〔PIYO_Z モジュール〕 (外部公開用ヘッダ) piyo_z.h (内部公開用ヘッダ) piyo_z_i.h で、こんな感じでヘッダファイルをインクルードさせます。 ======================================================== [hoge_a.c] -------------------------------------------------------- #include "hoge_a_i.h" #include "hoge_b_i.h" #include "piyo_z.h" ======================================================== [hoge_b.c] -------------------------------------------------------- #include "hoge_a_i.h" #include "hoge_b_i.h" #include "piyo_z.h" ======================================================== [piyo_z.c] -------------------------------------------------------- #include "hoge_a.h" #include "hoge_b.h" #include "piyo_z_i.h" ======================================================== また、この場合、こんな感じで内部公開ヘッダに外部公開ヘッダをインクルードしておきます。 ======================================================== [hoge_a_i.h] -------------------------------------------------------- #include "hoge_a.h" ======================================================== [hoge_b_i.h] -------------------------------------------------------- #include "hoge_b.h" ======================================================== [piyo_z_i.h] -------------------------------------------------------- #include "piyo_z.h" ======================================================== さて、上記のヘッダファイルの構築法ですが、下記のように考えればよいのではないでしょか。 (1) 最初は 1ファイルの小さなソースファイルでプログラムを作成。 この場合、ヘッダファイルを作成する必要はありません。 (2) 機能拡張していくと、1ファイルではメンテナンスしきれないくらいコードが肥大化してくるため、機能ごとに別ファイルにする (機能分割)。 この場合、機能ごとにヘッダファイルが必要になる。 (3) さらに機能部に対して機能拡張していくと、やはりコードの肥大化によりメンテナンスが困難になってくるため、機能部をモジュールごとに別ファイルにする (モジュール分割)。 この場合、必要に応じてモジュールごとのヘッダファイルを用意する。 また、(2) で作成したヘッダファイルのうち、分割したモジュールに関連する部分を「外部公開ヘッダ」、モジュール分割により新たに必要になったヘッダファイルを「内部公開ヘッダ」として作成する。 とりあえず、ご参考まで。 長々と失礼しました。
その他の回答 (6)
- fibre102
- ベストアンサー率20% (6/29)
プロトタイプ宣言の例です。 まず、xxx.cというファイルが一つだけの場合です。xxx.c内の関数fnc1()から同じくxxx.c内のfnc2()を呼び出すとき、fnc2()のほうが後ろに定義されてたら、fnc1()よりも前にfnc2()のプロトタイプ宣言を書きますよね。 次に、yyy.cというファイルがもう一つあって、その中のfnc3()からxxx.cのfnc2()を呼び出すとき、fnc3()の前にfnc2()のプロトタイプ宣言を書けばコンパイルは通りますが、本末転倒です。プロトタイプ宣言はコンパイル時の型チェックのために書くのに、yyy.cで好きなように書くと本来のxxx.cでの定義とずれる可能性があるからです。 そこで、fnc2()のプロトタイプ宣言は定義のあるxxx.cが提供することにして、xxx.hにfnc2()のプロトタイプ宣言を書きます。それを、yyy.cやその他、fnc2()を使いたいファイルがインクルードすれば、コンパイル時に本来の目的のfnc2()の型チェックを行えることになります。 プロトタイプ宣言以外の#defineやstruct定義も同様ではないでしょうか。各ファイル内でそれぞれ同じものを定義してもコンパイルは通りますが、提供元がヘッダとして公開してそれを共有することでファイル間で定義を一意のものとする、という感じではないでしょうか。 逆に言えば、他のファイルに公開したくないstructなどは.cファイルのほうに記述することで、隠蔽できます。
- graphaffine
- ベストアンサー率23% (55/232)
まともな回答は既にされているので、 邪道な解答を書きます。 ヘッダーファイルの話でないので質問の趣旨から外れるかもしれませんが。 状況 10本のプログラムがあり、main関数はほとんど同じ。 それらのmain関数は頻繁に変更され、変更内容は同じ。 このとき、main.cppでそのほとんど同じ内容を書き、各プログラムではそれをincludeするようにした。 異なる部分は#if definedを使って切り分けたが、 その部分もごくわずか。 このようにして、main.pp1本の修正だけで済むようになった。 p.s. 根本的な問題(似たようなプログラムをたくさん作るのがまずい)はおいときます。だって、私が作ったんじゃないですもの。
- jacta
- ベストアンサー率26% (845/3158)
ヘッダファイルの作り方は人によって方針が異なりますので、回答者間で矛盾した回答が得られる可能性が高いかと思います。それを踏まえて... まず、C専用か、C++専用か、C/C++両用かによって、若干作り方が異なります。 C専用の場合、ファイル名は~.hにすることが多いかと思います。C++で使うことを想定しなくてもよいので、比較的シンプルな作りになるかと思います。 C++専用の場合、ファイル名は~.hの他、~.hppや~.hhなどが使われます。クラス指定子や、インライン関数定義、(exportがサポートされないので)テンプレート関数定義などがヘッダファイルの中で行われます。 C/C++両用の場合、基本的にはC専用と同じですが、__cplusplusマクロが定義される場合には、外部宣言をextern "C" {~}で囲んだり、マクロの使用を最小限に抑えたりと、C/C++のどちらにも通用する作り方をする必要があります。 どの場合にも共通なのは、多重インクルードを抑止するための仕組みが必要です。 #ifndef XXX #define XXX ... #endif といった書き方ですが、ここで大切なのは、マクロ名XXXは下線(アンダースコア)で始めないことです。下線で始まるファイル有効範囲(C++では大域的前空間有効範囲)の識別子は処理系と標準ライブラリに予約されており、そうした識別子をマクロとして定義した場合の動作は未定義になってしまうからです。 もう一つは、(#endifの後など)ファイルの終端は必ず改行で終わることです。これも、空でないソースファイルが改行文字で終わらない場合の動作は未定義になるからです。 ヘッダファイルからヘッダファイルのインクルードについては意見が分かれるところですが、私の場合は積極的に行っています。共通する内容を複数のヘッダファイルに分散して記述すると保守性が下がることが理由です。 現在のコンパイラやPCの性能からすれば、プリプロセスに要する時間が問題になることはほとんどありませんが、どうしても気になる場合は、インクルードの際に、 #ifndef XXX #include "xxx" #endif XXX とすれば、無駄な処理を最小限に抑えることができます。
- buihyaku
- ベストアンサー率29% (97/326)
あるソースファイルで定義された関数を他のソースファイルでも使いたい場合に、その宣言をヘッダファイルに記述し、それを使用したいソースファイルでインクルード(取り込み)します。 また、複数のソースファイルで共通に使用される定義は複数のソースにそれぞれ同じ定義を記述するのではなく、ひとつのヘッダに記述してそれぞれのソースファイルの先頭でインクルードします。こうすることで、定義の内容があとで変わったときなどに1箇所だけ修正すればすむようになります。 ひとによって違う部分もあるとおもいますが、わりと一般的な暗黙のマナーとして、(すべておおまかなもので厳密に守らなければいけないというようなものではない) ○ヘッダファイルはだいたいひとつのソースファイルに対し、ひとつ作成します。 ヘッダファイル名はソースファイル名と同じで、拡張子を.hにします。 ○ヘッダファイルの先頭に以下のような2重インクルードを防ぐための#ifdefを定義するのが一般的です。 (HONYARARAはなんでもよいが他で使われていないことが保証されるもの。たとえばヘッダファイル名を使用) -------------- #ifndef __HONYARARA_H__ #define __HONYARARA_H__ ここにヘッダの内容 #endif /* __HONYARARA_H__ */ -------------- 同じヘッダを2回インクルードするとコンパイル時にすでに定義されています、というようなエラーになりますが、こうしておけばそれを回避できます。 ○ヘッダファイルに#includeで別のヘッダをインクルードするのは[できるだけ]避けます。 もちろんそれが必要な場合もありますので禁止というわけではありません。 ヘッダにヘッダをインクルードすると、必要以上にそのヘッダを参照するソースが増えるため、たとえばあるヘッダを修正すると、本来その必要のないソースまで大量のソースを再コンパイルしてしまう羽目になりかねません。 質問者さんの立場はよくわかりませんが、これらはいまはよく意味がわからなくてもマナーとして癖にしておくとよいとおもいます。
- kokorone
- ベストアンサー率38% (417/1093)
aaa.c というように、単一ファイルで構成される 場合、インクルードファイルは、不要です。 aaa.c bbb.c ccc.c など、ソースファイルが複数 あるような場合、 共通する#define定義・外部変数の定義/extern 宣言 または、bbb.cに存在する関数をaaa.cで参照するよう な場合の関数のextern宣言など、複数のソースファイ ルで共通的な定義をする場合、インクルードファイル を使用します。 下記URLが比較的わかりやすく書かれています。 「2.インクルード」を参照してください。
- NNori
- ベストアンサー率22% (377/1669)
ヘッダファイルは、最初からある物も含めてそのファイル中で使う変数や関数、クラスにどういうものがあるのかを記述してあります。 例えばa.ccというファイルの中で作ったクラスをb.ccというファイルで使うためには、そのクラスの名前やどのように呼び出す関数があるのかがわからないとb.ccというファイルを見ただけではわかりません。そこでそのクラスの呼出方等を記述した物がヘッダでありそれを参照することによって文法的に正しいかをチェックするわけです。