• 検索結果がありません。

自習 名前 ( 識別子 ) の有効範囲 , 局所変数 , 大域変数

ドキュメント内 新潟大学学術リポジトリ (ページ 105-110)

もし仮に変数や配列が大域的(global)でプログラム内のどの場所からでもアクセスでき るとしたら、そのプログラムを 同時に全て見渡して 各部分の役割を見定めない限り、そ のプログラムの動作を見極めることはできない。大域的な変数や配列はプログラムを分か りにくくする原因にもなる。 それゆえ、C言語においては、関数定義

関数値のデータ型 関数名 ( データ型 名前, . . . , データ型 名前 ) · · ·頭部 {

宣 言...

宣 言 ...

}

· · · 本体

の中で宣言され確保される変数や配列は、その関数定義の本体の中だけで使える局所的

.c 前処理 コンパイル

.i .o

.c .i .o

.c .i .o

・ ・

・ ・ ・ ・ ・ ・ ・

1 2

.o

.o

・ ・

.o

.o

・ ・

数学ライブラリ

自動的にリンカに渡さ れる標準ライブラリ

/lib/libm.a

/lib/libc.a ccコマンドの最後に

-lmオプションを付けると これらもリンカに渡される。

リンク 3

実行ファイル 実行ファイル 主記憶

ロード

(補助記憶内)

ソースファイル (一時的) オブジェクト ファイル (補助記憶内) (補助記憶内) (補助記憶内)

図 3: コンパイル作業の流れ

4.5. 自習 名前(識別子)の有効範囲, 局所変数, 大域変数 101

(local)なものとして扱われ、この関数の外部からはアクセスできない様になっている。

'

&

$

% 補足:

通常の場合は、関数定義の中で宣言されている変数や配列は、その関数が呼 び出された直後に領域が確保され関数実行が終了するとともに領域が解放さ れるので、関数の外部からアクセスしようにもできない。

printf()sin()も関数である。もし仮にこれらの関数の内部で使われて

いる変数に我々の作った関数からアクセスできるとしたら、プログラムの動 作が不安定になってしまう。

=変数や配列の有効範囲の局所性はプログラムの信頼性の上でも大切。

実際には、関数定義の本体部 (i.e.仮引数列に続く { と } で囲まれた部分)は、ブロック の一種と考えられる。

ブロックと複合文: C言語においては次の構造のものを複合文と呼び、そのうち実際に 宣言が1個以上含まれているものをブロックと呼ぶ。

{

宣 言...

宣 言 ...

}

複合文/ブロックは、1つの文が書ける所であればどこにでも置くことが出来るので、ブ ロックの中により小さなブロックが入り、その内側のブロックの中にまた別のブロックが 入り、... という入れ子構造 も可能である。プログラムの中に出来るこの「ブロックの 入れ子構造」に基づいて、変数等に付けた名前の有効範囲が次の様に決まる。

(規則1) どの名前(の領域)も、それが宣言されたブロックの中だけでアクセスできる。

(規則2) 外側のブロックで宣言された名前を内側のブロックで再定義すると、外側の名

前の領域(i.e.その名前を持った外側の変数)は内側のブロックからはアクセスできな

くなる。 '

&

$

% (規則1)の理由:

ブロック内で宣言された変数や配列の領域は、ブロック内の宣言の場所に制御が 移ると自動的に確保され、ブロックの出口に制御が移ると解放される。

ブロックの中で宣言され局所的に使われるこれらの変数を自動変数という。

大域的な名前:

• 関数名はそのファイルのどの場所からでもアクセスできる。

• 関数の外で宣言された変数や配列はそのファイルのどの場所からでもアクセスでき

る。 (外部変数, 外部配列という。)

=⇒ ファイル全体をブロックの一種と見なすことも出来る。

'

&

$

% 注意:

外部変数を使い過ぎると、副作用のために関数同士の独立性が なくなり、分かりにくいプログラムになる恐れがあります。

次の例題は、C言語における名前の有効範囲の規則を例示するものである。

例題 4.3 (名前の有効範囲, 外部変数) 次のCプログラムを実行するとどういう出力

が得られるか? 下の の部分に予想される出力文字列を入れよ。但し、

ここでは空白は と明示せよ。

[motoki@x205a]$ nl function-scope-of-name.c Enter

1 /* 名前の有効範囲、外部変数の理解のためのプログラム例 */

2 #include <stdio.h>

3 void sub(void);

4 int a = 1; /* 外部変数 */

5 int main(void) 6 {

7 int a = 22; /* 自動変数 */

8 printf("(1) %d\n", a);

9 { /* ブロックの始まり */

10 int a = 333;

11 printf("(2) %d\n", a);

12 } /* ブロックの終わり */

13 printf("(3) %d\n", a);

14 sub();

15 return 0;

16 }

17 void sub(void) 18 {

19 int b = 4444;

20 printf("(4) %d\n", a);

21 printf("(5) %d\n", b);

22 }

[motoki@x205a]$ gcc function-scope-of-name.c Enter

4.5. 自習 名前(識別子)の有効範囲, 局所変数, 大域変数 103

[motoki@x205a]$ ./a.out Enter

[motoki@x205a]$

(文法上の注意)

• プログラム3行目 の1つ目の void は関数 sub()の戻り値がないことを明示し、2つ

目の voidは関数 sub() の引数がないことを明示している。

• プログラム4行目 は、int型外部変数a を確保しその初期値を1とすることを指示し ている。

(考え方) プログラム9∼12行目,および6∼16行目, 18∼22行目 がブロックとなっている から、このプログラムの中に出来る「ブロックの入れ子構造」 を明示すると次の様にな る。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

/* 名前の有効範囲、外部変数の理解のためのプログラム例 */

#include <stdio.h>

void sub(void);

int a = 1; /* 外部変数 */

int main(void) {

int a = 22; /* 自動変数 */

printf("(1) %d\n", a);

{ /* ブロックの始まり */

int a = 333;

printf("(2) %d\n", a);

} /* ブロックの終わり */

printf("(3) %d\n", a);

sub();

return 0;

}

void sub(void) {

int b = 4444;

printf("(4) %d\n", a);

printf("(5) %d\n", b);

}

それゆえ、

• プログラムの4行目 で宣言された外部変数a は、同じ名前の変数が宣言された内側 の6∼16行目のブロックの外側で有効である。

• プログラムの7行目 で宣言された自動変数a は6∼16行目のブロックの入口で宣言さ れており、また同じ名前の変数が更に内側の9∼12行目のブロックでも宣言されてい る。従って、7行目の a は6∼8行目, 13∼16行目で有効となる。

• プログラムの10行目 で宣言された自動変数a は、9∼12行目のブロックの入口で宣 言されており、またこのブロックは別のブロックを含まない。従って、10行目の a は 9∼12行目のブロック内で有効となる。

• プログラムの19行目 で宣言された自動変数b は、18∼22行目のブロックの入口で宣

言されており、またこのブロックは別のブロックを含まない。従って、19行目の bは 18∼22行目のブロック内で有効となる。

(実行結果) 結局、プログラムの

8行目の a は 7行目で確保された a として、

11行目の a は 10行目で確保された a として、

13行目の a は 7行目で確保された a として、

19行目の a は 4行目で確保された a として、

20行目の b は 18行目で確保された b として 解釈されることになるから、実行結果は次の様になる。

[motoki@x205a]$ ./a.out Enter (1) 22

(2) 333 (3) 22 (4) 1 (5) 4444

[motoki@x205a]

ドキュメント内 新潟大学学術リポジトリ (ページ 105-110)