従って、例えば同じ関数を2回呼び出したとき、 1回目の呼び出しのときの変数の値は、 2回目の呼び出しのときまで保存されません。 例えば、次のプログラムでは、 sub1 の1回目の呼び出しで変数 i に 10 が代入されるものの、 2回目の呼び出しのときには i に 10 以外のものが入っています。
このような変数を自動変数と呼びます。 自動変数の宣言は通常次のように行います。
#include <stdio.h> void sub1(int f) { int i; /* sub1 の中だけで有効な変数(自動変数) */ if (f == 0) { i = 10; } else { printf("i = %d\n", i); } } void sub2(void) { int x = 20; } int main(void) { sub1(0); /* sub1 の i に 10 を入れる */ sub2(); sub1(1); /* sub1 の i を表示する */ /* i が保存されていれば 10 が表示されるはず */ return 0; }
auto は自動変数の宣言であることを示します。 auto を省略した場合、 ブロックの内側で宣言された変数は自動変数であると見なされます。 普通 auto は省略されます。auto int i;
ブロック内で変数された変数が ブロックの終了後も保存される用にするには、 static を付けて変数を宣言します。
このような変数を静的変数と呼びます。 静的変数の記憶領域の割り当ては、 プログラムの実行開始時に行われ、 プログラムが終了するまで保存されます。 上のプログラムにおいて sub1 の変数 i を static int i; と 宣言すれば、2回目の呼び出しのときにも i には 10 が入っています。static int i;
上の例で、 for によって繰り返されている { ... } の部分も ひとつのブロックです。 従って、この部分でも変数宣言を行うことができます。
#include <stdio.h> void sub(void) { int i; /* 関数 sub の中だけで有効 */ for (i = 0; i < 10; i++) { printf("%3d", i); } printf("\n"); } int main(void) { int i; /* 関数 main の中だけで有効 */ for (i = 0; i < 10; i++) { sub(); } return 0; }
上の例では、(2) のブロックで宣言されている変数 j は その内側のブロック (1) でも有効ですが、 ブロック (2) の外側では使えません。
#include <stdio.h> int main(void) { <---------------+ int i; | | for (i = 0; i < 10; i++) { <-----------+ | int j; | | | | for (j = 0; j < 10; j++) { <-------+ | | printf("%3d", j); (1) (2) (3) } <-------+ | | printf("\n"); | | } <-----------+ | | return 0; | } <---------------+
このような変数の有効範囲のことを スコープ(可視範囲) といいます。
外部変数
複数の関数の間で同じ変数を共有したいときは、
その変数を関数の外側の、
その変数を共有しようとする全ての関数より前で宣言します。
このような変数は外部変数と呼ばれます。 外部変数の記憶領域はプログラムの実行開始時に割り当てられます。 すなわち外部変数は静的変数です。
#include <stdio.h> int sum; /* 共有しようとする変数 */ void goukei(int n) { do { sum += n; /* 共有しようとする変数 */ } while (--n > 0); } void hyouji(void) { printf("合計は %d\n", sum); /* 共有しようとする変数 */ } int main(void) { sum = 0; /* 共有しようとする変数 */ goukei(100); hyouji(); return 0; }
外部変数の宣言が、 その変数を参照している部分より前にないときは、 その部分より前で 「どこかにそう言う変数があるよ」という宣言をします。
この宣言自体は i という変数に対してメモリを割り当てませんが、 どこか他のところで i が宣言されていることを示します。extern int i;
このようにファイルを分けた場合は、 次のようにしてコンパイルします。
goukei.c extern int sum; /* どこか他で宣言されている */ void goukei(int n) { do { sum += n; } while (--n > 0); }
hyouji.c #include <stdio.h> extern int sum; /* どこか他で宣言されている */ void hyouji(void) { printf("合計は %d\n", sum); }
main.c int sum; /* 共有しようとする変数の宣言 */ int main(void) { sum = 0; goukei(100); hyouji(); return 0; }
これによって main.c goukei.c hyouji.c をそれぞれコンパイルし、 作成されたオブジェクトファイルを結合して prog という実行ファイルを 作成します。cc main.c goukei.c hyouji.c -o prog
この例では、外部変数 sum (の実体)は main.c の中だけで宣言されており、 goukei.c hyouji.c では、
によって、それぞれのソースプログラムの中以外に sum が用意されていることを宣言しています。extern int sum;
一方、関数の外部で、
のように宣言した場合には、 この変数はそのソースファイルの それが宣言されている場所より後ろ以外からは参照されないことを示します。 例えば、上記の例の main.c において sum を次のように宣言すると、 sum が他から参照できなくなるため、 goukei.c および hyouji.c にある extern int sum; の実体がないことになり、 リンク時にエラーになります。static int sum;
main.c static int sum; /* このファイル内だけで使用される変数の宣言 */ int main(void) { sum = 0; goukei(100); hyouji(); return 0; }
関数プロトタイプ
課題48において、kadai48b.c に含まれる関数 inc を
kadai48a.c の関数 goukei 内で使用するために、
kadai48a.c の先頭で次のように宣言していました。
これまで例では、 関数は必ずそれを呼び出している部分より前で定義していました。 これは外部変数の宣言と同じ規則です。extern int inc(void);
従って、 関数の定義が同じソースファイルの関数を呼び出している部分より前に無い場合は、 外部変数同様その関数を extern を使って宣言してやる必要があります。 この場合、戻り値の型の他に、 引数の型も宣言します (関数の中身は必要ありません)。 これを関数プロとタイプと言います。
この例では関数 swap の戻り値の型が void(値を返さない)で、 2つの引数がいずれも int 型のポインタであることを示しています。
#include <stdio.h> extern void swap(int *, int *); /* 関数プロトタイプ */ int main(void) { int x, y; x = 10; y = 20; swap(&x, &y); printf("x=%d, y=%d\n", x, y); return 0; } void swap(int *a, int *b) { int t; t = *a; *a = *b; *b = t; }
extern void swap(int *, int *);