STM32勉強日記(2021/7/10):ヘッダファイルについての学習
今までで出来ていること
青ボタンスイッチを押すと、次の曲を再生する
やりたい事
割り込み処理の詳細について調べる
ここまでの学習事項についてまとめる?
ライブラリの作成
DFplayer用の自分の関数を作成する。
別ファイルに、.hでファイル作成し、色々なプログラムで活用出来るようにする。
PWM入力、音量変更プログラムを作成する。
今日やる事。
やはりキリのいいところまでは作ってしまいたい。
関数、ライブラリの作成方法などについて復習し、作成する。
出来ればコーディングルール的なものもしりたい。
結果:
ヘッダーファイルの作り方の勉強で終わった。 まだまだ時間がかかりそう。
ソースファイルとヘッダーファイルについて勉強していく。
多分、ヘッダーファイルをつくって、適用出来れば、他のプログラムにも適用が出来るようになるはず。
作る際の注意点:
ある変数「i」があったとして、この変数の有効範囲内では自由に「i」を使えて、範囲外では使えなくなることを思い出してください。この文法のおかげで、他のプログラマーが記述したソースの中で識別子「i」を使っていたとしても、自分が記述したソースで識別子「i」を使えるようになります
うーん、なんとなく、ソースファイルとヘッダーファイルの違いと、使い方については、少しだけ分かったけど。
どのような場合に分けるのだろうか。
メリットは?
ということで、それも調べる。
ざっくりいうと、大規模開発の場合のコスト(時間や電力)削減、切り分けによるエラーなどを探しやすくする。
機能ごとに分けるのが普通っぽい。
単語が色々出てくるが、その意味も分からないので調べないとかなぁ
やり方だけ先に調べて、やってみてしまうか、意味を調べていくか迷う。
先にヘッダファイルとして作った方が良いものと、その書き方について調べるか。
table:ヘッダーファイルとソースファイルの書くべきもの
ヘッダファイル ソースファイル 役割
多重インクルード防止 〇 × ヘッダファイルを多重にインクルードされるのを防止
型定義 〇 △ 構造体、列挙型、共用体のデータ型定義
プロトタイプ宣言 〇 △ 関数のプロトタイプ宣言
グローバル変数の外部参照宣言 〇 × 外部ファイルへ公開するグローバル変数の参照宣言
グローバル変数の外部参照宣言変数定義 × 〇 グローバル変数の実体定義
関数定義 × 〇 関数の実体定義
ヘッダファイル、ソースファイルに書くもの:〇
ヘッダファイル、ソースファイルに書いてはいけないもの:×(特に注意するもの)
ソースファイルに書く場合もあるが基本かかない?:△
面倒だが、全部調べていこうと思う。 それぞれ理解する必要はるだろうなぁ・・・
多重インクルード防止
多重インクルード防止:
ヘッダファイルが複数ある場合に、同じ名前のものがあるとエラーを起こす
その為に、多重にインクルードされるのを防止する必要がある。
書き方
プリプロセッサの「条件コンパイル」と「マクロ定義」を利用して書きます。この記述を「多重インクルード防止」という
何か重要な処理に先立って、その準備として実行される処理を「前処理(preprocess)」と呼んでいます。プリプロセッサは、本来のコンパイル処理に先立って、その準備となる処理を実行するので、前処理プログラムと呼ばれるわけです。C 言語に関するものであることを明示したいときには「C プリプロセッサ」と呼びます
プリプロセッサで処理させる命令をプリプロセッサ命令またはプリプロセッサディレクティブといいます。
プログラムの出力までの処理:
プログラム→ プリプロセッサ処理 →コンパイル → コンパイル結果の出力
ここのプリプロセッサで処理されるのが、プリプロセッサ命令といわれるものっぽい。
code:c
//よく使われるプリプロセッサ命令
//例
//PIを使用すると、3.14159265が使用されたことになる。
v = 3 * PI;
//出力結果: v=3×3.14159265=.42477795
//if 0と1を切り替えることで、実行するプログラムの切り替えが出来る。 コメントアウトの代わりにもなる。
//実行されない
//実行される
//実行される
//実行されない
code:c
#ifdef //条件コンパイル:プログラムの中で不要な部分をカットする事が出来る。 //if(もし) def(#define)が定義されていたら、実行する
//#define DEBUG_ON
int main(void)
{
printf("Hello"); //#define DBUG_ONが無いため、実行されない。
printf("World"); //実行される
return 0;
}
int main(void)
{
#ifdef DEBUG_ON //#define DBUG_ONがあるため、実行される。 printf("Hello");
printf("World");
return 0;
}
ふと思ったんだけど。ホームページやブログはいつ消えるか分からないから、PDFに保存しておいた方がよいなと。
これからは、PDF保存していく。
# ifdefの理解が出来た気がする。これを使って、多重インクルードを防止する。
code:c
//#define DEBUG_ON
int main(void)
{
printf("Hello"); //#define DBUG_ONが無いため、実行される。
printf("World");
return 0;
}
int main(void)
{
printf("Hello"); //#define DBUG_ONがあるため、実行されない。
printf("World");
return 0;
}
ウェブ上で簡単にCを実行、確認できるコンパイラ?があるので、それでやってみる。
多重インクルード:
code:c
// sub.hというファイルを作成する場合
#ifndef SUB_H //#define SUB_Hが実行されて無ければ、#define SUB_Hを実行し、 #define SUB_Hが既に実行されてあれば、#define SUB_Hを実行しない。 // ・・・ヘッダ定義の詳細・・・
疑似的に、やってみると。
code:c
int main(void)
{
int x = D_SUB_NUM;
printf("出力結果:%d",x);
}
//出力結果:100 とでる。
//上側の#ifndef SUB_Hから#endifが無ければ、出力結果:200となる。
つまり、下の条件を入れておけば SUB_Hが先に定義されている場合、処理を実行しないので、同じ名前の定義があった場合のエラーになるのを防ぐことが出来る。
code:c
#ifndef SUB_H //#define SUB_Hが実行されて無ければ、#define SUB_Hを実行し、 //#define SUB_Hが既に実行されてあれば、#define SUB_Hを実行しない。
// ・・・ヘッダ定義の詳細・・・
マクロ定義
マクロとは:C言語におけるマクロとは、プログラム中の文字列をあらかじめ定義した規則にしたがって置換する機能のことをいいます。マクロは、#defineというプリプロセッサ指令により定義します
マクロには、単純にプログラム中の文字列を指定した文字列に変換する「オブジェクト形式マクロ」と引数をとってあたかも関数のように使用できる「関数形式マクロ」の2種類があります。
オブジェクト形式マクロ
code:c
//#define 文字列1 文字列2
//下記プログラムではdefine定義により、プリプロセスでプログラム中のDATA_NUMという文字列が全て10という文字列に置き換わります。
/**
* @file ObjectLikeMacroSample.c
* @brief 簡単なオブジェクト形式マクロの例
*/
//マクロ定義
{
92, 94, 90, 80, 81, 82, 84, 79, 75, 95
};
int main(int argc, char *argv[])
{
int i;
int total = 0;
for (i = 0; i < DATA_NUM; i++)
{
}
printf("Total : %d¥n", total);
printf("Average : %d¥n", total / DATA_NUM);
return 0;
}
利点:
1.保守性の向上(修正がしやすい)。データの要素数が10から変更になった場合、define定義のみを修正すればよい。
2.可読性の向上(プログラムが読みやすい)。プログラム中に10という数字が直接書かれているとその数字の意味がわからない。DATA_NUMと書かれていれば、データの個数を指していることがすぐにわかる
関数形式マクロ
code:c
//#define マクロ名(引数の並び) 置き換えられる処理
//MAX(10, 11)というプログラムが、プリプロセスにおいて、((10) > (11) ? (10) : (11)) に置換されます。その結果、このプログラムをコンパイル、実行すると、11と表示されます。
/**
* @file FunctionLikeMacroSample.c
* @brief 簡単な関数形式マクロの例
*/
/**
* 最大値取得
* 与えられた2つの引数のうち大きい値を返す
* @retval 値1と値2のうち大きな方の値
*/
#define MAX(a, b) ((a) > (b) ? (a) : (b)) int main(int argc, char *argv[])
{
printf("%d¥n", MAX(10, 11));
return 0;
}
? : 条件演算子 a ? b : c aが真ならbが実行、aが偽ならcが実行
つまり、a>bなら(a)が実行 a>b出ないなら、(b)が実行。 今回はa=10 < b=11より、b=11になった。
メリット:
マクロ置換が行われるので、関数呼び出し時のオーバーヘッドがない反面、多く使用されるとコードサイズが増加するという特徴を持ちます。
code:c
//------------------------------------------------
// マクロ定義(Macro definition)
//------------------------------------------------
//------------------------------------------------
// 型定義(Type definition)
//------------------------------------------------
typedef struct
{
long num1;
} S_SUBINFO;
と、ここまでやったが、今日は、この後予定があるので、ここまでとする。
次は、マクロ定義から、型定義、プロトタイプ宣言などを勉強していく。
.h ファイルを作成するにも色々勉強することがあり、中々大変ですね・・・・・・
予定だと、ここら辺はまだやる予定ではなかったが、どうしようかな。
明日までに、プログラミングを一区切りして、来週には回路設計する予定だったけど。
先にAD変換やって、回路設計と部品出しやって発注するか、少しプログラミングの時間を伸ばして、こっちを引き続きやるか。
どっちにせよ、後でやることになるけど。
4連休を考えると、来週は先に回路設計して、部品は発注しておきたいかもしれない。
明日は、とりあえずこのまま進めて、来週の土日は回路設計~部品発注まで終わらせよう。
作業:3時間くらい