c言語のソースファイルとヘッダーファイル

ソースとヘッダー

ソースファイルの分割

c言語は勿論ソースファイルだけでも作ることはできますが、一つのファイルだけで完結さるより分割して構成した方がコードを使いまわしたり整理しやすなったりと無駄が省けて恩恵が多いです。
ただ理解が漠然としていたのでしっかりと確認します。
ファイルをインクルードするのは
#include
を使用します。

スコープ

スコープ(英: scope, 可視範囲)とは、ある変数や関数などの名前(識別子)を参照できる範囲のこと。通常、変数や関数が定義されたスコープの外側からは、それらの名前を用いるだけでは参照できない。このときこれらの変数や関数は「スコープ外」である、あるいは「見えない」といわれる。

Wikipedia

識別子=シンボル
変数とか関数の名前。
nm コマンドで表示されるやつ。
一つのスコープにつき同一のシンボルは一つだけ使用できる

#include
int a = 0; //グローバル変数
int func(void){
int a = 1; //ローカル変数
printf(“a is =%d\n”,a);
}
int main(void){
int a = 2;//ローカル変数
func();
}

 

以下のコードをビルドして実行すると

a is =1

という結果が出ます。
aというシンボルを3つ定義したわけですが、結果はfuncで定義した値a = 1になっています。
つまり入れ子になったスコープの中で同じ変数が使用された場合、より内側のスコープで定義された変数が優先されます。

大域スコープ(グローバルスコープ)

静的なメモリ領域に配置されプログラムの全てのスコープから参照可能な大域(グローバル)変数。
色んなプログラムが参照するデータを保管しているけど、それ故グローバル変数が変更されデータが改竄されたり破壊されるかわからないため好まれない。

extern グローバル変数や関数名

で外部ファイルの参照ができ、これを外部参照と呼びます。

ファイルスコープ

大域スコープと似たようなもので静的なメモリ領域に配置されれる。
ただし参照できるのはそのファイルのみに限られる。
c言語ならstatic修飾をした変数や関数がこれにあたる。

static グローバル変数

こうするとファイルスコープになります。
上記の大域スコープと違い他のファイルからのアクセスを防ぐことができます。

局所スコープ

関数内でのみ参照可能なローカル変数。
メモリのスタック領域に配置されstatic修飾などがされていない場合関数処理が終わると同時に破棄される。

static修飾がされている場合は静的メモリ領域に保持されるので次回同じ関数が呼ばれた時に参照することができます。
この場合の初期化は一番初めにローカル変数が呼ばれた時にだけ実行されます。
例えば何度も関数を呼び出すプログラムでその関数が処理を行った回数を記録しておきたいときなど利用できます。

#include
int test1 = 0;
int func(void){
int test2 = 1;
return 1;
}
int main(void){
int test3 = 3;
return 1;
}

nmコマンドでファイルのシンボルテーブルを表示させてもスタックに積まれるローカル変数は表示されません。
つまり局所スコープで、関数の終わりと同時にローカル変数は捨てられるので参照することができません。
今度は少しだけ上記のコードを変えてみます。

#include
int test1 = 0;
int func(void){
static int test2 = 1;
return 1;
}
int main(void){
int test3 = 3;
return 1;
}

func()関数内で定義しているローカル変数test2にstatic修飾してnmコマンドでシンボルテーブルを表示してみるとtest2のシンボルが今度は表示されます。
グローバル変数と同じように静的領域に展開されているのでシンボルテーブルに表示されています。
ただしグローバル変数と違ってstaticなローカル変数にアクセスできるのはそれを宣言した関数のスコープだけです。

分割コンパイル

複数のオブジェクトファイル、Test.h,Test.c,main.cに分割してソースコードを作った場合、ひとつづつファイルをコンパイルしオブジェクトコードを生成しそのオブジェクトファイル同士をリンクします。

gcc ファイル名 ファイル名 ファイル名
3つのファイルをリンクして一つのファイルにして処理が完了します。

Test.h

#include
void func(void);

Test.c

#include "Test.h"
void func(void){
printf("hello world");
}

main.c

#include "Test.h"
int main(void){
func();
}

まずは3つに分割したファイルをコンパイルし「ファイル名.o」のオブジェクトファイルを3つ作ります。
後はgccでリンクするだけです。

二重インクルード

上記のTest.hのようにするだけでもOKなんですが、ヘッダーファイルは色んなファイルから参照されます。
例えば#include で標準入出力関数のヘッダーはTest.hをインクルードしたファイルでも参照されている可能性があります。
そうなると2重にを読み込んでしまうことになりが重複してしコンパイラエラーが発生してしまいます。
それを防ぐためにc言語のマクロを使用します。

#ifndef INCLUDED_Test_h_
#define INCLUDED_Test_h_


void func(void);



#endif

#ifndefは以下のファイル名が定義されていなければ#endifまでのコードをファイルへ挿入します。
定義されていれば無視されます。
この場合他のファイルがTest.hを読み込んでいた場合、そのファイルにも#define INCLUDED_Test_h_が定義されていることになるので#ifndef INCLUDED_Test_h_以下のコードは無視されることいなり二重にvoid func(void);が宣言されることを防ぎます。
ヘッダーファイルには基本的にこのマクロが挿入されます。
ちなみにifndefはIf not define(定義されていなければ)の略です。

コンパイラが実行可能ファイルを作るまで
コンパイル→実行可能ファイル 簡単な流れ GCCを使用します。 C言語でソースコードを記述しコンパイルした時どんな風になっているのか。 「コンパイルしたら動くファイルができる!」 という理解でも問題はありませんが、もっと深く知るこ...
オブジェクト指向言語のメモリ管理
オブジェクトプログラミングのメモリ管理 メモリの管理 ごく簡単に。 静的領域:グローバル変数、命令コード。 ヒープ領域:インスタンス、バッファ。 スタック:メソッドに渡す引数など。 静的領域 このメモリ領域は最初にプログラ...
プログラミングおすすめ書籍

おすすめ書籍

なぜプログラムが動くのか。
平易な言葉で学べますが、初心者には難しいかもしれません。
でも読み終わった頃にはなんとなく理解しているでしょう。
初心者には家にあって損はありません。

UNIXの思想を学べます。
高尚なタイトルですが、コードはおろか難しい言葉も殆どありません。取っ掛かりに最適でその後のプログラミングに対する理解を一変させます。
なぜならこの思想はプログラミングそのものだからです。
一読の価値ありで、これは良書なので今後紹介したいと思っています。

まだまだありますがまたの機会に紹介したいと思います。

コンピューター
スポンサーリンク
himazinをフォローする
私の頭の上の消しゴム

コメント

タイトルとURLをコピーしました