関連項目:
関数化ヘッダファイル化を行ったところでプログラムの
動作自体は何も変わらない。
「ヘッダファイル化」という作業が増えるだけで
成果物に変化は無い。
これだけ聞くと意味の無い作業の様に思える。
しかし、この作業により後々で
作業効率に大きな影響を及ぼす。
「楽をするためにはどんな努力も惜しまない」
今回もそんな話。
ヘッダファイルの使い方
#include <stdio.h>
#linclude "iodefine.h"
これは授業だったり、サンプルプログラムだったりで見ているはずです。
<>と
""の使い分け
<>は開発環境が用意したヘッダファイルを読み込むときに使います。
Pathが通っていると言われるフォルダ内にあるファイルに対して使います。
””は各プロジェクト用に作成したファイルを読み込むときに使います。
基本的には各プロジェクトの
メインとなるソースファイル(*.cファイル)があるフォルダ内にあるファイルに対して使います。
「iodefine.h」はプロジェクトが用意したファイルですが、
開発するマイコンの型式によって異なります。同じ「
iodefine.h」という名前でもマイコンの種類によって
中身が異なるのです。
「iodefine.h」はプロジェクトを新規作成する際に使用するマイコン用の物がプロジェクトのあるフォルダ内に生成(多分どこかからコピー)されます。
ということで「iodefine.h」はプロジェクト用に作成したファイル扱いとなり、メインとなるソースファイルがあるフォルダ内にあるので""で読み込みます。
ヘッダファイルの機能
もちろんなにかしらの機能を読み込む際に使う機能です。
「ヘッダ」は各ファイルの
冒頭部分を表し、ワード等で各書類の上の方に日付等を入れる際に使用する領域「ヘッダ」と意味合いは同じです。
あるソースファイルA(ヘッダファイルでも可)にてヘッダファイルBをincludeする(読込む)と、
ヘッダファイルBに書いてある内容をソースファイルAの
includeした位置に書いたのと同じ扱いになります。
そして、ヘッダファイルは
自作することができます。
C言語の場合、ソースファイル等で使用する関数をヘッダファイルに記載しておきます。
ヘッダファイルはプロジェクト内の
様々なソースファイルにてincludeすることを意識して作成する必要があります。
ヘッダファイルの使い道
なぜヘッダファイル(ファイル分割)を使うのか。
目的は関数化に準じます。
プログラムの見やすさの改善と一度作製した機能の使いまわしです。
見やすさの改善関数化によりメインプログラムの見やすさが改善しましたが、
一つのファイル内に記述される内容量は変わっていません。
例えば関数化の際に紹介したsinのマクローリン展開の場合、一度関数化をしてしまえば以降はこの
マクローリン展開の中身を見る必要はほとんど無いはずです。しかし、ファイル内に関数の実体を書いたままではメインプログラムを編集するときに支障があります。
メインプログラム内の
特定の個所を編集する場合、ソースファイルをスクロールして
編集したい個所を探すことになるとおもいます。同じソースファイル内に関数の実体が多数あるとスクロールバーが細くなり、マウスでスクロールバーを操作するとページが一気に動いて
目的の場所になかなかたどり着けません。
関数の実体およびプロトタイプ宣言を別ファイルに移すことでメインのソースファイルがみやすくなります。
機能の使いまわし一つのソースファイル内に関数を作製した場合、
そのファイル内で関数を使いまわすことができます。しかし、
そのファイル以外では関数を使用できません。関数を別ファイルに移すことで、そのファイルを読み込んだソースファイルで関数を使うことができるようになります。また、三角関数なら三角関数で、マイコン特有の特殊機能なら特殊機能でそれぞれヘッダファイルを作成しておくことでそのファイルを他のプロジェクトにコピーすることで関数の機能を使いまわすことができます。
メインのソースファイルに全ての機能を書いていた場合、そのソースファイルをコピーしてしまうとメインの関数までコピーされるほか、必要の無い機能までコピーされてしまいます。コピーされるだけなら大きな問題にはなりませんが、コピー先に同じ名前の異なる機能の関数がある場合エラーになります。
近い機能ごとにヘッダファイルを作成し、必要な機能のみコピーして使用することで機能の重複を防ぎます。
ヘッダファイルの中身
ヘッダファイルには
プロトタイプ宣言と
一部のデファイン(定義方法の一種)を書きます。
ヘッダファイル内に
関数の中身を書いても動くには動きますが、複数のソースファイルでincludeすると
トラブルになります。
同様にヘッダファイル内で
グローバル変数を宣言していろいろな個所で使用することもできますが、複数のソースファイルでincludeするとこれも後ほど
トラブルになります。
ヘッダファイル(*.h)に書く内容
・
関数のプロトタイプ宣言・共通で使うdefine文
・extern付のグローバル変数宣言(参照)
・構造体の型枠の宣言※多重読込防止が必要
基本的には何回出てきても問題がない、「
プロジェクト内にどこかに関数または変数があるので使えますよ」という
参照文です。
ソースファイル(*.c)に書く内容
・ソースファイル内でのみ使用する変数定義
・
関数の実態・ソースファイル内でのみ使用するdefine文
・extern無しのグローバル変数定義(実体)
・使用する構造体の定義
変数や関数の実体など何回も出てくるとマズイものという理解で良いでしょう。
ヘッダファイルに書く内容
・関数のプロトタイプ宣言これについては
関数化の回での説明の通りです。
関数のプロトタイプ宣言は同じ内容であれば何度記述しても問題ありません。
関数の実体はソースファイル側に書きます。
・共通で使うdefine文例えば円周率3.141593を毎回書くのはめんどくさいのでPIと書きたい場合
#define PI 3.141593
と定義しておけば PI と書くことで、PIと書いた箇所が3.141593に置き換えられます。
ただし
#define PI 3.141593
;と書かないように注意のこと。
x=sin(PI*0.5);
と書いた場合
x=sin(3.141593
;*0.5);
と置き換えられてエラーになります。
同じ名称のdefine文が出てきた場合、
最後に出てきたdefine文が有効になるようです。
共通で使いたい場合はヘッダファイルに、あるソースファイル内でのみ使いたい場合はソースファイルに書きましょう。
・extern付のグローバル変数宣言意味合いとしては関数のプロトタイプ宣言と似ています。
変数の定義をヘッダファイルに書いた状態で複数のソースファイルでincludeすると宣言が複数記述された扱いになります。同じ名前の変数定義が複数個所存在するとエラーとなりビルドできません。そこで、グローバル変数の実体はソースファイル内で定義し、「
どこかにその変数があるので使えますよ」という宣言のみをヘッダファイルで行います。
exterm 変数名;
と記述すると「どこかにその変数があるので使えますよ」という扱いになります。
ただし、グローバル変数の使用は可能な限り避けた方が良いでしょう。グローバル変数として使用したい変数は重要な役割を持っているはずです。この重要な変数に間違った値を書き込んでしまうとCPUが暴走します。
グローバル変数を何の工夫もせずどこでも使える状況にしておくとこの暴走を誘発する危険性が残ります。特にどこかのソースファイルで処理した値をメインのソースファイルから
読出しのみ行いたい場合でも間違って書き込みを行ってしまう可能性があります。重要な変数へのアクセスには何かしらのチェックを用意しておくべきです。
他のソースファイルに記載された変数にアクセスしたい場合はアクセス専用の関数を用意しておき、間違った入力をした場合は実行しないような処理を行います。
・構造体の型枠の宣言※多重読込防止が必要「こういった内容を持つ構造体を今後使っていきますよ」という宣言文です。
変数の定義同様に同じ名前の型枠宣言は複数回記述することはできません。
型枠の宣言をヘッダファイルに書いた状態で複数のソースファイルでincludeすると宣言が複数記述された扱いになりエラーになります。ただし、型枠の宣伝をソースファイルに記述すると他のソースファイルでこの型枠を利用することができないため、型枠の宣言はヘッダファイルに書かなければいけません。
そこでファイル内の一部を
多重読込防止にする必要があります。
ヘッダファイルを多重読込防止する場合
#ifndef~#endifを使用します。
#ifは通常の分岐条件として使用するifとは違い、
プログラムをビルドする時のみ反応します。
#ifndefは
if not define を意味します。
#ifndef _DEFINE_H_ #define _DEFINE_H_ 1 各種処理 各種処理 各種処理#endifと記述すると、
_DEFINE_H_が定義されていない時のみ
#ifndef~#endif が処理されます。
最初にこの個所を処理する時には
_DEFINE_H_が定義されていないため
#ifndef~#endifが処理されます。その際に
_DEFINE_H_が定義されます。
2回目以降にこの処理を行う際は
すでに_DEFINE_H_が定義されているため
#ifndef~#endifは処理されません。
ということで各処理部分に型枠の宣言文を書きます。
また「_DEFINE_H_」部分は何でも構わないのですが、基本的には作成するヘッダファイル名をベースとして使用します。
ヘッダファイルに記述するのは型枠の宣言(記入項目が用意されたひな型)のみで、実際に使用する構造体定義(各項目を記入する用紙)はソースファイルに書きます。
※構造体の使い方・使い道の詳細は今後説明します。