C には、変数の格納方法を指定する「記憶クラス指定子」として、次の4つがある。
- auto
- static
- extern
- register
auto
auto は自動変数を宣言するためのもので、自動変数(automatic variables)とは、要するに単なるローカル変数のこと。ローカル変数はデフォルトで auto になるので、明示的に auto を使うことはまずない。
static
ローカル変数は、関数から抜けるとメモリ領域から削除されるけど、static を指定すると、ローカル変数の内容を複数の関数呼び出しにまたがって保持することができる。関数呼び出しのたびに初期化される普通のローカル変数と違って、static な変数の初期化は1回しか行われない。そして、値はプログラムが終了するまで保持される。
次のプログラムは、関数 f を10回呼び出している。関数 f の中の static な変数 count の値が、呼び出しのたびに初期化されることなく、前回の呼び出しの時の値を保持していることがわかる。
#include void f(void); int main(void) { int i; for (i = 0; i < 10; i++) { f(); } return 0; } void f(void) { static int count = 0; count++; printf("count: %d\n", count); }
takatoh@nightschool $ ./sample_11_1a count: 1 count: 2 count: 3 count: 4 count: 5 count: 6 count: 7 count: 8 count: 9 count: 10
extern
プログラムのサイズが大きくなった場合、C ではファイルを複数に分けることができる。このとき複数のファイルからアクセスする必要があるグローバル変数があったとする。グローバル変数は1回しか宣言することが出来ないので、複数のファイルそれぞれに宣言を書くことは出来ない。かといって、宣言がないとコンパイラが変数の宣言を見つけられずにエラーになってしまう。
こういう場合、通常の方法でグローバル変数を宣言するのは1つのファイルだけにしておいて、その他のファイルでは extern 指定子を使って宣言する。extern はそのグローバル変数が別のファイルで宣言されていることをコンパイラに伝える役割をする。次の例は、ファイル sample_11_1b1.c で宣言されたグローバル変数 count を、ファイル sample_11_1b2.c でも使えるように extern 指定子を使っている。
#include int count; void f1(void); int main(void) { int i; f1(); /* set value to count */ for (i = 0; i < count; i++) { printf("%d\n", i); } return 0; }
#include extern int count; void f1(void) { count = rand(); }
takatoh@nightschool $ gcc sample_11_1b1.c -c takatoh@nightschool $ gcc sample_11_1b2.c -c takatoh@nightschool $ gcc sample_11_1b1.o sample_11_1b2.o -o sample_11_1b takatoh@nightschool $ ./sample_11_1b 1 2 3 4 5 (略)
register
register を指定すると、コンパイラは変数をメモリではなく CPU のレジスタに格納しようとする。初期の C では、register を適用できるのは char か int のローカル変数あるいはポインタに限られ、これらの変数は CPU のレジスタに格納されていた。ANSI C 標準ではこれらの制限が緩められて必ず CPU のレジスタに格納されるという要件は撤廃されているけど、依然として register 変数をアクセス時間が最小になるような方法で格納するように規定されている。だから、char と int はレジスタに格納されることになる。
ただし、レジスタの数は限られているので、レジスタに格納できない変数は、たとえ register が指定されていても通常の変数に変えられてしまう。だから本当に高そくにアクセスしたい変数を慎重に選ぶ必要がある。一般的に、1つの関数につき2つの変数は register 指定できると考えていいようだ。