内 容
記憶クラスと記憶領域
プログラムのメモリ構成

では、ロードされたプログラムはメインメモリにどのように配置されるのでしょうか。プログラム本体(機械語)と変数はそれぞれ別のメモリ領域に格納されます。また、変数もローカル変数やグローバル変数などその種類によって、配置される領域が決まっています。メモリのどこに配置するかを決めることを記憶クラスと言います。左図にそのメモリ配置の構成図を示します。(ポインタ変数は後期講義に登場します。)
記憶クラス指定子
変数が記憶領域のどこに配置されるかを決めるのは記憶クラス指定子です。記憶クラス指定子は変数宣言の際に、次のようにデータ型の前に付加します。記憶クラス指定子 データ型 変数名 <,変数名,・・・>;
記憶クラスは、配置される記憶領域を決めるだけではなく、プログラム実行中におけるその変数の有効範囲と記憶寿命を決めることにもなります。 以下に記憶クラス指定子と記憶クラス、記憶寿命の関係を表す一覧表を示します。
記憶クラス指定子 | 変数の名称 | 記憶領域 | 記憶寿命 |
---|---|---|---|
auto | 自動変数 | スタック領域(自動記憶域) | コードブロック内 |
static | 静的変数 | スタティック領域(静的記憶域) | プログラム中 |
extern | 外部変数 | (既にある外部変数を示す) | プログラム中 |
register | レジスタ変数 | レジスタ、またはスタック領域 | コードブロッックない |
※ レジスタ変数は、CPU内部のレジスタと呼ばれる小容量の記憶装置を利用する。アクセス速度が主記憶装置よりも高速であるため、処理速度を優先する処理に利用される。しかし、容量が小さいため、もし、容量を超えて変数宣言された場合は、その部分についてはスタック領域に確保される。
自動変数(auto記憶クラス指定子)
関数内で記憶クラス指定子を指定しない変数は、自動的に自動変数とみなされます。(今まで関数内で宣言していた変数は自動変数だったわけです。)自動変数はメモリのスタック領域と呼ばれる領域に格納されます。その有効範囲と記憶寿命は宣言されたコードブロック内であり、その変数が利用される間のみ領域が確保されることになります。例えば、ある関数で宣言された自動変数は関数の呼び出しと同時にスタック領域に格納領域が確保され、関数の処理が終了すると同時にその格納領域は消失します。自動変数は、変数宣言によってそのデータ型に応じた領域がメモリに確保されますが、その値は明示的な初期化が行われなければ不定となります。すなわち自動的に0で初期化されるといったことは行われません。したがって、必ずその変数を利用する前に値を確定させなければなりません。
次のプログラムは、main関数から関数func1を繰り返し呼び出してそれぞれの自動変数の値を表示する例です。main関数で定義されている変数iも、関数func1で定義されている変数xも、どちらも自動変数なのでメモリのスタック領域に保存されるます。main関数の自動変数iの記憶寿命は、main関数が処理される間、すなわちプログラム実行中有効で、その値は0、1、2と変化します。しかし、関数func1の自動変数xの記憶寿命は関数func1が処理されている間で、処理が終了するとスタック領域から削除されてしまいます。ですから、呼び出される度に変数xの値は初期値1となります。
automatic_variable.c
#include <stdio.h> void func1(void); int main(void) { int i; //自動変数 for(i=0; i<3; i++){ printf("%d: ",i); func1(); } return 0; } void func1(void) { int x=1; //自動変数 printf("func: x = %d\n", x++); //自動変数のインクリメント }
静的変数(static記憶クラス指定子)
記憶クラス指定子staticが付与され宣言された変数を静的変数と呼びます。静的変数は静的記憶域(スタティック領域)に格納され、その記憶寿命はプログラムの実行中になります。静的変数は、自動変数とは異なり明示的に初期化が行われなかった場合、自動的に0で初期化が行われます。
次のプログラムは、先のプログラムに新たに関数func2を追加したものです。関数func2は変数xが静的変数として宣言されている以外は関数func1と同様に変数xの値をインクリメントして表示するだけです。関数func2の変数xの有効範囲は関数func2の中だけですが、静的変数なのでその記憶寿命はプログラムの実行中となります。したがって、関数func2の処理が終了しても静的領域に格納された値は維持されるため、呼び出される度にその値を1、2、3と増加させていきます。
static_variable.c
#include <stdio.h> void func1(void); void func2(void); int main(void) { int i; //自動変数 for(i=0; i<3; i++){ printf("%d: ",i); func1(); func2(); } return 0; } void func1(void) { int x=1; //自動変数 printf("func: x = %d\n", x++); //自動変数のインクリメント } void func2(void) { static int x=1; //静的変数 printf("func: x = %d\n", x++); //静的変数のインクリメント }
グローバル変数
グローバル変数とは関数の外で宣言される変数で、その有効範囲はプログラム全体でした。記憶寿命もstaticが付いてはいませんが、変数宣言からプログラムの終了までの間でとなります。すなわち静的変数です。(staticの付いたグローバル変数は別の意味で利用されるので注意が必要です。詳しくは後述する。)global_variable.c
#include <stdio.h> int x = 1; //グローバル変数 void func(void); int main(void) { int i; //自動変数 for(i=0; i<3; i++){ printf("%d: ",i); func(); } return 0; } void func(void) { printf("func: x = %d\n", x++); //静的変数のインクリメント }
プログラムのメモリ配置
変数が記憶クラス指定子によってメインメモリにどのように配置されるかは、プログラムを実行させた時のそれぞれの変数のメモリアドレスを知る必要があります。変数のメモリアドレスは、アドレス演算子 &を変数名に付与することで得ることができます。次のプログラムは、自動変数がスタック領域に、グローバル変数が静的記憶域に格納されていることを確認するプログラムです。また、main関数と関数funcが配置されるコード域の位置も確認することができます。実行して表示されたメモリ・アドレスからそのメモリ配置図を描いてみてください。
memorymap.c
#include <stdio.h> char g = 99; //グローバル変数(静的記憶域) char func(char x) //引数(ローカル変数) { char y; //ローカル変数(自動変数) static char z; //ローカル変数(静的変数) y = x * 2; puts("--- func memory map ---"); printf("stack : x = %d (%p)\n", x,&x); printf("stack : y = %d (%p)\n", y,&y); printf("stactic : z = %d (%p)\n", z,&z); return y; } int main(void) { char a,b,c; //自動変数(スタック) a = 11; b = 22; c = 33; puts("--- main memory map ---"); printf("code : main (%p)\n", &main); printf("code : func (%p)\n", &func); printf("static: g = %d (%p)\n", g,&g); printf("stack : a = %d (%p)\n", a,&a); printf("stack : b = %d (%p)\n", b,&b); printf("stack : c = %d (%p)\n", c,&c); func(a); return 0; }
外部変数
複数のソースコードを横断して、グローバル変数や関数を利用したい場合は外部変数を利用します。外部変数として宣言するためにはグローバル変数にextern指定子を付与します。他のファイルの変数を利用する
次の二つのファイルからなるプログラムは、ファイルafile.cで定義されているグローバル変数exaと関数exfunc()をもう一つのファイルbfile.cで利用するために、externを付けて定義している例です。afile.c
#include <stdio.h> int exa; //グローバル変数 void exfunc(void) { exa = 777; printf("afile: exa = %d\n",exa); }
bfile.c
#include <stdio.h> extern void exfunc(void); //別ファイルで定義された関数を利用する extern int exa; //別ファイルで定義された変数を利用する:外部変数 int main(void) { exfunc(); printf("bfile: exa = %d\n", exa); return 0; }
ヘッダファイルで共有する
他のファイルで利用したい外部変数をヘッダファイルにまとめた例です。ヘッダファイルにまとめておくことで、特定のファイルだけではなく、そのヘッダファイルをインクルードしたファイルでも共有することができる点で、大きなメリットがあります。headerfile.h
extern int exa; extern void exfunc(void);
afile.c
#include <stdio.h> #include "headerfile.h" //ヘッダファイルの読み込み void exfunc(void) { exa = 777; printf("afile: exa = %d\n",exa); }
bfile.c
#include <stdio.h> #include "headerfile.h" //ヘッダファイルの読み込み int main(void) { exfunc(); printf("bfile: exa = %d\n", exa); return 0; }
staticを付けてグローバル変数
static指定子を付与したグローバル変数は、他のファイルからは見えなくなります。もし、複数のファイルで同じグローバル変数名が使われていたとしても、static指定子を付けておけば別の変数として扱うことができます。headerfile.h
//external int exa; extern void exfunc(void);
afile.c
#include <stdio.h> #include "headerfile.h" //ヘッダファイルの読み込み static int exa; //他のファイルからは見えない void exfunc(void) { exa = 777; printf("exfunc: exa = %d\n",exa); }
bfile.c
#include <stdio.h> #include "headerfile.h" //ヘッダファイルの読み込み static int exa; //他のファイルからは見えない int main(void) { exa = 111; exfunc(); printf("bfile: exa = %d\n", exa); return 0; }
・staticありグローバル変数 内部リンケージ 別ファイルの同名変数とは異なる
expand_lessBack to TOP