ソースとヘッダー
ソースファイルの分割
c言語は勿論ソースファイルだけでも作ることはできますが、一つのファイルだけで完結さるより分割して構成した方がコードを使いまわしたり整理しやすなったりと無駄が省けて恩恵が多いです。
ただ理解が漠然としていたのでしっかりと確認します。
ファイルをインクルードするのは
#include
を使用します。
スコープ
識別子=シンボル
変数とか関数の名前。
nm コマンドで表示されるやつ。
一つのスコープにつき同一のシンボルは一つだけ使用できる
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(定義されていなければ)の略です。


コメント