Linux講座にようこそ。このページは「C言語プログラミング入門 - 第10章.データの有効範囲と寿命を規定する記憶クラス」です。
関数間のデータのやりとりは引数と返り値で行えますが、これらにはちょっとした欠点があります。関数間でやりとりするデータが多い場合、引数の数が多くなりますが、そうするとコーディングが面倒になりますし、分かりにくいプログラムになりがちです。また、返り値で返せるデータは1個のみです。
そこで、もっと簡単にデータのやりとりができるように、変数や配列を関数間で共有することができます。つまり、1つの変数の値を複数の関数で参照や更新ができるということです。また、逆に、変数や配列を特定の関数でのみ使えるようにすることもできます。これらを指定するのが記憶クラスと呼ぶものです。
変数名や関数名などの名前を識別子と呼んでいますが、記憶クラスは識別子の有効範囲と有効期間を指定します。
記憶クラスは識別子の宣言時に指定します。宣言の方法については後ほど説明します。
有効範囲には内部変数(ローカル変数)と外部変数(グローバル変数)の2種類があります。
内部変数は関数内やブロック内で宣言し、それらの中だけで使用可能です。従って、使用範囲を限定できますので、他の関数で誤って変数の値を変更してしまったというような不良を防ぐことができます。今までの例題プログラムで使っていた変数や配列は全て内部変数です。
一方、外部変数は関数の外側で宣言し、複数の関数で参照・更新が可能です。従って、関数間でデータ共有が簡単に行えます。しかし、このことは誤って変数の値を更新してしまったというような不良が発生しがちですし、不良箇所を見つけ出すのも大変です。
このようなことから、基本的には内部変数を使い、どうしても必要な場合のみ外部変数を使うようにした方がよいでしょう。
有効期間には自動変数と静的変数の2種類があります。
自動変数は宣言時にメモリ上に確保され、使用を終了するとメモリ上から破棄されてしまいます。使用終了はブロックや関数の実行が終了した時点です。破棄されたメモリはリサイクルされますので、メモリを有効に使うという点では優れています。今までの例題プログラムで使っていた変数や配列は全て自動変数です。
一方、静的変数はプログラム起動時にメモリ上に確保され、プログラム実行中は存続します。また、静的変数は宣言する箇所により関数内静的変数と関数外静的変数の2種類があります。
関数内静的変数は関数の内側で宣言し、有効範囲は関数内のみになります。一方、関数外静的変数は関数の外側で宣言し、有効範囲は宣言した行からソースファイルの最後までになります。
変数や配列および、関数の宣言形式についてはすでに説明しましたが、これらの宣言に記憶クラスを指定することができます。
今までは変数の宣言を関数の先頭で行っていましたが、ブロックの先頭で行うこともできます。もっとも、関数も一つのブロックですので、関数の先頭ということはブロックの先頭ということもできます。今までのように、ブロックの先頭で宣言したものは内部変数です。内部変数の有効範囲はブロック内です。
また、初期値を指定しなかった場合の値は不定になりますので注意してください。
int main(void) { int v1; ← (1) char v2; ← (1) /* func関数のプロトタイプ宣言 */ int func(int p_v1, char p_v2); ← (2) v1 = func(10, v2); ← (3) } /* func関数の宣言 */ int func(int arg1, char arg2) ← (4) { int v3; ← (5) char v2; ← (5) for(int idx = 0; idx <= arg1; ++idx) ← (6) { } if(v3 <= 10) { int v4 = 0; ← (7) } }
int idx = 0;
はfor文のブロックの内側で宣言をしていることになります。従って、変数idxの有効範囲はfor文のブロック内だけです。関数の外側で宣言すると外部変数になります。外部変数の有効範囲はソースファイル上の宣言した行からソースファイルの最後までになります。
また、初期値を指定しなかった場合の値は0になります。(整数型の場合は0、実数型の場合は0.0、文字型の場合はヌル文字('\0')になります)
int v1 = 0; ← (1) int func1(int p_v1, char p_v2); ← (2) int func2(void); ← (2) int main(void) { int v1; ← (3) int v2; char v3; v1 = func1(v2, v3); ← (4) v1 = func2(); ← (5) } int func1(int arg1, char arg2) ← (6) { v1 = func2(); ← (7) } int func2(void) ← (8) { }
外部変数を別のソースファイルに定義した関数でも有効にしたい場合は、そちらのソースファイルで記憶クラス指定子のexternを指定して宣言します。externを指定した変数は外部変数として確保してあることをコンパイラに伝えるだけで、メモリ上に領域を確保しません。上記例題の外部変数v1を別のソースファイルでも使いたい場合は次のように指定します。
extern int v1; int func3(void) { int v4; v4 = v1 + 10; ← 変数v1は外部変数です }
ブロックの先頭で、記憶クラス指定子のautoまたは、記憶クラス指定子を指定しない場合は自動変数になります。autoは互換性維持のためにあるようなので通常は指定しませんので、結局、ブロックの先頭で、記憶クラス指定子を指定しないということになります。つまり、宣言は内部変数と同じになりますので、内部変数と自動変数は同じものを着目する性質により呼び分けているだけです。
自動変数はブロックの実行が終了すると、メモリ上の領域は解放されてリサイクルされてしまいます。
int main(void) { auto int v1; int v2; }
記憶クラス指定子のstaticを指定すると静的変数になります。静的変数はプログラム開始時にメモリ上に確保され、プログラム実行中は存続します。
静的変数は宣言する箇所により、関数内静的変数と関数外静的変数の2種類があります。関数内静的変数はブロックの先頭で宣言しますので、有効範囲はブロック内のみです。それに対して関数外静的変数は関数の外側で宣言しますので、有効範囲は宣言した行からソースファイルの最後までになります。
外部変数と関数外静的変数は有効範囲が似ていますが、異なる点があります。外部変数は記憶クラス指定子のexternを指定すれば、別のソースファイルでも使うことができますが、関数外静的変数は使うことができません。
static int v1; ← 関数外静的変数です static int func1(int p_v1, char p_v2); ↑ 関数内静的変数として宣言していますので、このソースファイル以外では呼び出せません int main(void) { int v2; v1 = func1(v2, 'a'); } static int func1(int arg1, char arg2) { static int v3 = 0; ← 関数内静的変数です int v4 = 0; ← 自動変数です v3 = arg1 + v4; ← この間数が終了しても、代入された値は保持されます }
半角文字列を逆順にして出力します。StrRev関数は引数で指定した文字列を逆順にした配列を返します。(入力文字は半角で100文字以下とし、スペースやタブ等の空白文字は含みません)
$ ./ex10_1.prg 文字列を入力してください ==> Hello!!. 逆順:.!!olleH $