9.6 自動変数と静的変数、局所変数と大域変数
これまでのプログラムの説明では、定義、宣言された変数の使用範囲、有効範囲等に関して明確にされていませんでした。C言語では、変数は有効範囲が異なる局所変数(ローカル変数)と大域変数(グローバル変数)の2つの種類に分けられます。
これまでの変数はすべて関数の中で宣言されていて、その関数の中でのみ有効でした。これらの変数は関数の外からは通常の使用方法ではアクセスすることができません。この様な変数を局所変数と呼びます。この局所変数は、その使用の目的により動的局所変数と静的局所変数に分かれます。また局所変数は関数だけでなくブロック内でも局所的に定義できます。
●動的局所変数
動的局所変数は、その関数またはブロックが呼ばれた時に自動的に生成され、その関数またはブロックから制御が離れるときに自動的に消滅します。これまでのプログラムの例に現れた変数は全て動的局所変数です。
#include<stdio.h> main() { sub(); sub(); } sub() { int a=0; printf("%d ",a); a= a+ 1; printf("%d \n",a); }●静的局所変数
静的局所変数は、それが宣言されている関数あるいはブロックに対しては局所的ですが、動的変数と異なり、関数やブロックの呼び出しに関係なくいつも静的に存在すします。静的局所変数の宣言は、変数の前にstaticと書きます。
静的局所変数も、予め定めておいた値をプログラムの実行前にもたせておくことができます。初期値を持たせておけば、その関数やブロックが最初に呼ばれるときにその値が参照されますが、2回目以降は初期値で初期化されず、前回の値が残ります。
#include<stdio.h> main() { sub(); sub(); } sub() { static int a=0; printf("%d ",a); a= a+ 1; printf("%d \n",a); }●大域変数
C言語には、もう1つの変数の形態として大域変数があります。大域変数は全ての関数またはブロックからアクセスすることができる広域変数です。大域変数は全ての関数またはブロックの外側で定義します。大域変数の定義の仕方は動的局所変数と同じです。
この外部変数を使用しようとする場合には、その使用にさきがけて宣言が必要です。この宣言は、変数の宣言の前にexternと書きます。しかし、通常、大域変数の宣言が、それをアクセスするブロック(関数)よりも前にあり、かつ、同一ファイル内で定義されているならば、ブロック(関数)内でのextern宣言は省略することができるます。ただし、プログラムがいくつかのファイルに分かれて存在するとき、つまり、別のファイル内で定義されている大域変数を使用するときにはextern宣言が必要です。大域変数を使ってリスト9.12と同様な結果になるプログラムをリストCに示します。
#include<stdio.h> int a=0; main() { sub(); sub(); } sub() { printf("%d ",a); a= a+ 1; printf("%d \n",a); }●ブロック内の局所変数
関数の中で局所変数が独立して存在したように、whileなどの{}の中、つまりブロック内にも独立して局所変数をつくれます。リスト9.13のプログラムとその実行結果よりブロック内の変数の使用範囲をみて下さい。このプログラムの中で使用している変数はint型のi,jのみです。 ブロックごとに変数の内容がどのように変化するかに着目します。
リスト9.13 │ リスト9.13の実行結果 /* blk.c */ │ i=1 j=2 #include│ i=3 j=2 main() │ i=1 j=3 { │ int i = 1; │ ┌──────────┐ int j = 2; │ │{ ブロック#1 │ printf( "i=%d j=%d\n", i, j ); │ │ int i=1; │ { │ │ ┌───────┴─┐ int i = 3; │ │ │{ ブロック#2 │ printf( "i=%d j=%d\n", i, j ); │ │ │ int i=3; │ j = 3; │ │} │ │ } │ └──┤ │ printf( "i=%d j=%d\n", i, j ); │ │} │ } │ └─────────┘ まず、関数mainのブロック#1に入ります。ここでは、int型の変数i=1、j=1を定義しています。このブロック#1では、ここで定義された変数i、jを使用することができます。次にブロック#1からブロック#2に入ります。ブロック#2ではその外のブロックで定義された変数は使用することができます。リスト9.13ではブロック#2で変数iが再び定義されていますから、iに関してはブロック#2独自の変数が用意されることになります。
リスト9.14にもう一例示します。
リスト9.14 │ リスト9.14の実行結果 /* blkn.c */ │ ********** int main(void) │ ********** { │ ********** int i; │ ********** for(i=0;i<10;i++) │ ********** { │ ********** int i; │ ********** for(i=0;i<10;i++) │ ********** printf("*"); │ ********** printf("\n"); │ ********** } │ return 1; │ } │●動的局所変数の例
リスト9.11 動的局所変数の動作 │ リスト9.11の実行結果 /* nstat1.c */ │ 0 0 0 0 0 0 0 0 0 0 #include│ void sub(); │ main() │ { │ int i; │ │ for(i=0;i<10;i++) │ sub(); │ } │ void sub() │ { │ int j=0; │ printf("%d ",j++); │ } │ ●静的局所変数の例 リスト9.12 静的局所変数の動作 │ リスト9.12の実行結果 /* stat0.c */ │ 0 1 2 3 4 5 6 7 8 9 #include │ void sub(); │ main() │ { │ int i; │ │ for(i=0;i<10;i++) │ sub(); │ } │ void sub() │ { │ static int j=0; │ printf("%d ",j++); │ } │ ●大域変数の例 リスト9.15 │ リスト9.15の実行結果 /* nstat0.c */ │ 0 1 2 3 4 5 6 7 8 9 void sub(); │ int j; │ void main() │ { │ int i; │ │ j=0; │ for(i=0;i<10;i++) │ sub(); │ } │ void sub() │ { │ printf("%d ",j++); │ } │ ●例、グローバルで共有する /* program seiseki(sei.c) */ static char *kamoku[] = {" kokugo", " suugaku", " rika", " syakai", " eigo"}; int k = 0, num[50], score[5][50], goukei[50], kamoku_goukei[5]; float heikin[50], kamoku_heikin[5]; void main() { void data_input(),data_calc(),data_print(); data_input(); if(k <= 0) return; data_calc(); data_print(); } void data_input() { int i; while(k < 50){ printf("num = "); scanf("%d", &num[k]); if(num[k] <= 0) break; // 負の出席番号を入力すると終了 for(i = 0; i < 5; i++){ // 5教科のループ printf("%8s = ", kamoku[i]); scanf("%d", &score[i][k]); } printf("\n"); k++; } return; } void data_calc() { int i, j; for(j = 0; j < k; j++){ goukei[j] = 0; for(i = 0; i < 5; i++) goukei[j] += score[i][j]; heikin[j] = goukei[j] / 5.0; } for(i= 0; i < 5; i++){ kamoku_goukei[i] = 0; for(j = 0; j < k; j++) kamoku_goukei[i] += score[i][j]; kamoku_heikin[i] = kamoku_goukei[i] / (float)k; } } void data_print() { int i, j; printf("\n"); printf(" bangou "); for(i = 0; i < 5; i++) printf("%8s", kamoku[i]); printf(" goukei heikin\n"); for(j = 0; j < k; j++){ printf("%7d ", num[j]); for(i = 0; i < 5; i++) printf("%8d", score[i][j]); printf("%8d%8.2f\n", goukei[j], heikin[j]); } printf(" goukei"); for(i = 0; i < 5; i++) printf("%8d", kamoku_goukei[i]); printf("\n"); printf(" heikin "); for(i = 0; i < 5; i++) printf("%8.2f", kamoku_heikin[i]); printf("\n"); } 9.7 再帰呼び出し 局所変数は関数間で独立して存在します。さらに動的局所変数は同じ関数同士でも独立して存在します。つまりa()という関数内で動的局所変数nが定義されているとすると、a()という関数から自分自身a()を呼び出したとき、呼ぶ側の関数と呼ばれた側の関数のそれぞれに独立した動的局所変数nが存在します。変数が独立していれば、同じ関数間でも他の関数と同様に呼び出して使うことができます。このような自分自身を呼び出すことを再帰呼出しと呼びます。 前述のリスト9.4のようなプログラムは再帰呼び出し(リカーシブコール)を用いて書くことができます。リスト9.16ではrfac()内で使用される変数xが関数が呼ばれる毎に新しく準備されます。 リスト9.16 /* rfac.c */ int main(void) { int i,rfac(); printf("i=?"); scanf("%d",&i); printf("%d ! = %d\n",i,rfac(i)); return 1; } int rfac(int x) { if(x == 1) return(1); else return(x * rfac(x-1)); } ●例題[行列の計算] 行列積を求めるプログラムを考えてみましょう。これも典型的な縦横計算になります。リスト24の処理の手順としては、任意の2組つの行列を読み込み、行列同士の積の計算を行ないます。 データ入力は最大10行10列の行と列の値を2行列分について読み込み、それから各行列の要素をそれぞれ入力サブルーチンを用いて読み込むようにします。行列積の性質上一方の列数と他方の行数は等しくなければなりません。 要素の入力サブルーチンは、行列の各要素を1行1列、1行2列、・・・と、指定した行、列まで順に読み込もます。 行列積の計算は一方の行列の行と他方の行列の列の各要素を、順に掛けて加えていき、それを新しい行列の要素として配列に書き込んでいきます。 計算がすむと、計算された行列を出力サブルーチンを用いて表示します。結果をみよすくするために行列の縦線(|)をずれないように表示するくふうをしています。 <リスト24>行列積の計算 /* matrix product mp.c */ #include #define max 10 main() { void m_input(),m_output(); int a[max][max],b[max][max],c[max][max], l,m,n,i,j,k; printf("A(L,M) :input L (max=%d)=",max); scanf("%d",&l); printf("A(L,M),B(M,N):input M (max=%d)=",max); scanf("%d",&m); printf("B(M,N) :input N (max=%d)=",max); scanf("%d",&n); m_input('A',l,m,a); m_output('A',l,m,a); m_input('B',m,n,b); m_output('B',m,n,b); for (i=0;i #include typedef struct { int sho,amari; } WARI; void main() { WARI *a,*wari(); a=wari(10,3); printf("%d %d \n",(*a).sho,(*a).amari); getch(); free(a); } WARI *wari(int a, int b) { WARI *c; c = malloc( sizeof(WARI) * 2); (*c).sho = a / b; (*c).amari = a % b; return c; }