プログラムが大きくなると1つのファイルだけでは管理しにくくなります。
そんなときはソースファイルを関連する機能別にファイルに分割して保存すると管理が楽です。
このファイル分割時に作るのがヘッダファイルです。
実務で使うような大きなプログラムは複数のヘッダファイルとソースファイルで構成されています。
ヘッダファイルとは宣言を記したファイル
プログラムが数百行に及んでくるとこんな悩みが出てきます。
これらはコードを別ファイルに分割すれば解決できます。
具体的には分割したい変数や関数などをヘッダファイルと別のソースファイルに移動します。
ヘッダファイルhead.hには移動したい変数や関数などの宣言を、ソースファイルclac.cにはそれらの定義を書きます。
ファイル分割することで機能ごとにファイルを分けたり、必要なファイルだけをコンパイル対象にしたりと管理しやすくなります。
ヘッダファイルの作り方・コンパイル方法
例として構造体、グローバル変数、関数を持つ以下のソースファイルを分割してみます。
例1:
ソースファイル「main.c」
#include <stdio.h> //グローバル変数 int NUM=1; //構造体 typedef struct { int id; char name[30]; }person; //関数 int plus(int x, int y) { if(NUM==1) return x+y; else return -1; } void main() { printf("NUM=%d\n",NUM); person sato={0,"Sato Hiroyuki"}; printf("ID=%d, Name=%s\n",sato.id,sato.name); printf("3+2=%d\n",plus(3,2)); }
実行結果
NUM=1
ID=0, Name=Sato Hiroyuki
3+2=5
-
参考【C言語】構造体の使い方をわかりやすく解説!宣言・初期化・代入方法など
構造体とは変数の一種です。 構造体を使うと関連がある複数の変数をまとめて扱えるので、変数の管理がしやすくなる上にコードが見やすくなるメリットがあります。 構造体とは何かわかりやすく解説 構造体を使うと ...
続きを見る
-
参考【C言語】関数をわかりやすく解説!引数・戻り値とは?呼び出し方は?
C言語、C++は関数なしでは成り立ちません。 関数を作ることで作業を効率化できますし、コードの修正も容易になります。 この記事では関数の基本的な使い方をわかりやすく解説します。 関数とは何か、なぜ必要 ...
続きを見る
Step1 関数のプロトタイプ宣言を作る
まずは分割したい関数のプロトタイプ宣言を作っておきます。
例2:
ソースファイル「main.c」
#include <stdio.h> //グローバル変数 int NUM=1; //構造体 typedef struct { int id; char name[30]; }person; //プロトタイプ宣言 int plus(int x, int y); void main() { printf("NUM=%d\n",NUM); person sato={0,"Sato Hiroyuki"}; printf("ID=%d, Name=%s\n",sato.id,sato.name); printf("3+2=%d\n",plus(3,2)); } //関数の定義 int plus(int x, int y) { if(NUM==1) return x+y; else return -1; }
実行結果
NUM=1
ID=0, Name=Sato Hiroyuki
3+2=5
Step2 ヘッダファイルを作る
ヘッダファイルを新たに作り、構造体と関数のプロトタイプ宣言を移動します。
加えてグローバル変数のextern宣言も書いておきます。
グローバル変数の定義はソースファイルに残します。
-
参考【C/C++】externとは?関数・変数・配列・構造体との使い方
複数のソースファイルで共通のグローバル変数を使いたい時に利用されるのがexternです。 通常、変数は宣言と定義が同時に行われますが、externを使うと定義は行わずに宣言だけを行えます。 exter ...
続きを見る
例3:
ソースファイル「main.c」
#include <stdio.h> #include "head.h" //グローバル変数の定義 int NUM=1; void main() { printf("NUM=%d\n",NUM); person sato={0,"Sato Hiroyuki"}; printf("ID=%d, Name=%s\n",sato.id,sato.name); printf("3+2=%d\n",plus(3,2)); } //関数の定義 int plus(int x, int y) { if(NUM==1) return x+y; else return -1; }
ヘッダファイル「head.h」
//グローバル変数のextern宣言 extern int NUM=1; //構造体の宣言 typedef struct { int id; char name[30]; }person; //プロトタイプ宣言 int plus(int x, int y);
ヘッダファイルには宣言だけを記述し、拡張子を「.h」にしてソースファイルと同じフォルダに保存しておきます。
printfなどの関数は使わないので「#include <stdio.h>」を書く必要はありません。
ソースファイルの先頭には「#include "ヘッダファイル名"」と書き、どのヘッダファイルを使うのか明らかにしておきます。
stdio.hのような標準ヘッダと違って自作のヘッダファイル名はダブルクォーテーション(")で囲います。
Step3 インクルードガードを記述
ヘッダファイルは複数のファイルでインクルードされ、何度も実行されることが多いです。
すると同じ宣言が何度も実行され、コンパイルエラーになることがあります。
これを防ぐためにヘッダファイルにはインクルードガードのためのマクロを書きます。
インクルードガードの書き方
#ifndef マクロ名
#define マクロ名
プロトタイプ宣言、extern宣言など
#endif
マクロ名はすべて大文字で「ヘッダファイル名(拡張子なし)_H」のように付けることが多いです。
例4:
ヘッダファイル「head.h」
#ifndef HEAD_H #define HEAD_H //グローバル変数のextern宣言 extern int NUM=1; //構造体の宣言 typedef struct { int id; char name[30]; }person; //プロトタイプ宣言 int plus(int x, int y); #endif
最初にヘッダファイルを実行したときはHEAD_Hが定義されていないので、if文内の処理を実行します。
次にヘッダファイルを実行した時は「#define HEAD_H」でHEAD_Hが定義されているので、if文には入りません。
よってif文の中身は一度しか実行されないようになっています。
Step4 ソースファイルをもう1つ作る
この時点ではまだmain.cにグローバル変数と関数の定義が残っています。
これらは別途ソースファイルcalc.cを作ってそちらに移動します。
例3:
ソースファイル「main.c」
#include <stdio.h> #include "head.h" void main() { printf("NUM=%d\n",NUM); person sato={0,"Sato Hiroyuki"}; printf("ID=%d, Name=%s\n",sato.id,sato.name); printf("3+2=%d\n",plus(3,2)); }
ヘッダファイル「head.h」
#ifndef HEAD_H #define HEAD_H //グローバル変数のextern宣言 extern int NUM; //構造体 typedef struct { int id; char name[30]; }person; //プロトタイプ宣言 int plus(int x, int y); #endif
ソースファイル「calc.c」
#include "head.h" //グローバル変数の定義 int NUM=1; //関数の定義 int plus(int x, int y) { if(NUM==1) return x+y; else return -1; }
新しく作ったソースファイルcalc.cの先頭にも「#include "head.h"」を書いておきます。
calc.cではprintfなどの標準ライブラリ関数を使わないので「#include <stdio.h>」は書かなくて大丈夫です。
Step5 ファイルをまとめてコンパイル
作成したすべてのファイルをまとめてコンパイルします。
しかし従来の方法では複数ファイルをまとめてコンパイルできないので、方法を以下で解説します。
始めにターミナルのカレントディレクトリをソースファイルが保存されているフォルダにする方法を解説します。
まずターミナルに「cd 」(最後にスペースが入る)と打ち込みます。
ターミナルが出ていない場合は画面上部の「ターミナル」をクリックし、「新しいターミナル」をクリックして開きます。
次にソースファイルが保存してあるフォルダを開き、ソースファイルの1つをターミナル上にドラッグアンドドロップします。
すると以下のような状態になるので、最後の「\(ソースファイル名).c」だけを消します。
下のように「cd (ソースファイルがあるフォルダのアドレス)」となったらEnterキーを押します。
「>」の左にソースファイルのフォルダのパスが表示されれば成功です。
次にターミナルに以下のフォーマットでコマンドを入力します。
C言語(gcc)の場合:
gcc コンパイルしたいソースファイル名(複数の場合はスペース区切り) -o EXEファイルの名前(拡張子なし)
C++(g++)の場合:
g++ コンパイルしたいソースファイル名(複数の場合はスペース区切り) -o EXEファイルの名前(拡張子なし)
例えばコンパイルしたいソースファイルがmain.cとcalc.cで作りたいEXEファイル(実行ファイル)がmain.exeの場合は以下のように書きます。
ヘッダファイル名はソースファイル内でインクルードされているで、ここで改めて書く必要はありません。
入力できたらEnterキーを押します。
特にエラーが無ければメッセージは表示されません。
エラーが出たら修正して再度同じコマンドを入力してEnterを押します。
一度入力したコマンドはターミナルを選択した状態で↑キーを押すと再度表示されます。
コンパイルが完了するとソースファイルがあるディレクトリにEXEファイルが作られます。
Step6 EXEファイルを実行
コンパイルが終わったらターミナルに以下のフォーマットでコマンドを入力します。
./EXEファイル名
実行したいEXEファイルがmain.exeの場合は以下のように入力します。
Enterを押すと実行結果が表示されます。
ファイル分割する前と同じ結果が得られれば成功です。
ヘッダファイルに変数定義を書いてはいけない
ヘッダファイルに変数定義を書くとコンパイルエラーの原因になります。
コンパイルすると1つのソースファイルあたり1つのオブジェクトファイルが作られます。このオブジェクトファイルをすべて統合(リンク)することでプログラムが作られます。
C言語、C++において定義はプログラム全体を通して一度しか書いてはいけないというルールがあります。
しかしヘッダファイルは複数のソースファイルにインクルードされるので、オブジェクトファイルには変数定義が書かれてしまいます。
インクルードガードが書いてあってもif文の結果によっては変数定義が実行できるよう、if文の中身が書かれてしまうのです。
するとリンク時に複数のオブジェクトファイルに同じ定義が書かれていることが発見されてエラーとなってしまいます。
定義はソースファイルの内1つだけに書き、ヘッダファイルには変数のextern宣言を書きましょう。
ヘッダファイルにおける構造体の書き方
構造体の宣言はヘッダファイルに記述できます。
extern宣言を使えば構造体の定義も記述できます。
詳しくはこちらで解説しています。
C言語で行き詰まったら…
-
C言語・C++がわからない時に質問できるサイト・サービス5選
C言語を独学で勉強しているけど内容がイマイチわからない。もはや何がわからないのかもわからない。 C++のエラーが解決できない。ググってもわからない。わかる人に解説してほしい。 こんなお悩みにお答えしま ...
続きを見る