プログラミング論
変数のスコープ,記憶クラス. メモリ動的確保. http://www.ns.kogakuin.ac.jp/~ct13140/ProgC/概要
• 変数のスコープ – 重要.おそらく簡単. • 記憶クラス – 自動変数(auto)と静的変数(static). – スコープほどではないが重要. C-2変数のスコープ
ブロック
• C言語では,{ から } をブロックという. void main(){ for(…){ if(…){ } } if(…){ } } C-4変数の宣言
• グローバル変数 – 関数の外(当然ブロックの外)で変数の宣言を行う. C, C++に依らず同じ. – 全ての関数内で使用可能. • ローカル変数 – C言語では, 変数の宣言は,ブロックの先頭でのみ行える. – C++では, 変数の宣言は,ブロック内の任意の位置で行える. – 宣言されたブロック内でのみ使用可能. C-5変数のスコープ
• 変数が使用できる範囲を"SCOPE"と言う. • SCOPEの広さは,変数を宣言した場所により異な る. • 変数宣言が含まれているブロックで最も狭いもの が選ばれる. – 変数は,宣言された中括弧{}の内側でのみ使用可 能.括弧の外では使えない. 括弧の内側には入れるが, 括弧の外側には出られない. C-6ローカル変数とグローバル変数
#include <stdio.h> int a; void main(){ int b; } void func(int c){ int d; } グローバル変数 ローカル変数 ローカル変数 ローカル変数 C-7ローカル変数
#include <stdio.h> int a; void main(){ int b; b = 7; printf("b = %d¥n", b); } void func(int c){ int d; b = 8; printf("b = %d¥n", b); } int bのスコープ範囲 NG NG OK OK C-8ローカル変数
#include <stdio.h> int a; void main(){ int b; d = 7; printf("d = %d¥n", d); } void func(int c){ int d; d = 8; printf("d = %d¥n", d); } int dのスコープ範囲 OK OK NG NG C-9ローカル変数
#include <stdio.h> int a; void main(){ int b; c = 7; printf("c = %d¥n", c); }void func(int c){
int d; c = 8; printf("c = %d¥n", c); } int cのスコープ範囲 OK OK NG NG int cは, 「ブロックの外で 宣言されている」 ように見えるが, SCOPEの範囲は, 関数内です. ご注意あれ. C-10
グローバル変数
#include <stdio.h> int a; void main(){ int b; a = 7; printf("a = %d¥n", a); } void func(int c){ int d; a = 8; printf("a = %d¥n", a); } グローバル変数 プログラム内の 全ての関数内で, 参照可能. int aのスコープ範囲 OK OK OK OK C-11ローカル変数(ブロック内宣言)
void main(){ int i; for(略){ int j; } } int iは内側(ピンク)ブロックに入れるが, int jはピンクブロックの外に出られない. C-12ローカル変数(ブロック内宣言)
void main(){ int i; if(略){ int j; } } C-13ローカル変数(ブロック内宣言)
void main(){ int i; { int j; } } 上記のように,for文やif文などなく 唐突に { } でブロックを作成しても問題ない. C-14ローカル変数(ブロック内宣言)
void main(){ int i; { int j; } } 上記の違いは,int jのスコープの広さ. void main(){ int i, j; }スコープの広さの善し悪し
• 一般に,以下のように考えられている. – スコープは狭いほどよい. – グローバル変数は良くない • グローバル変数や広いスコープは,間違い(バグ )の原因になりやすい. – 変数の値がどこで変更さているのか把握しづらくなる – グローバル変数が多いと,各箇所で「理解しなくては ならない変数」の数が増えて辛くなる.同じ名前の変数が2個以上あったら
• 同じ名前の変数が2個以上あったら,スコープの 狭い変数が勝つ – 感覚的には,近くで宣言されている変数が勝つ. • ただし,混乱の元なので,このようなことは行わな いことを強く推奨する. C-17 #include <stdio.h> int a = 3; void main(){ printf("a = %d¥n", a); } a = 3 実行結果 C-18 #include <stdio.h> void main(){ int a = 7; printf("a = %d¥n", a); } a = 7 実行結果 C-19 #include <stdio.h> int a = 3; void main(){ int a = 7; printf("a = %d¥n", a); } a = 7 実行結果 C-20 #include <stdio.h> int a = 3; void main(){ int a = 7; { int a = 9; printf("a = %d¥n", a); } printf("a = %d¥n", a); } a = 9 a = 7 実行結果 正しいプログラムだが, 「同じ名前の変数が 2個以上存在する」など, 混乱の元. このようなプログラムは 避けることを勧める. C-21同じ名前の変数が2個以上あったら
• 以下の様に,同一ブロック内で同名の変数を2個 以上宣言するのはNG. void main(){ int x; int y; int x; x = 7; } int xが 2個ある. NG! どっちのint xなのか 分からない. 実行不可能! C-22変数の記憶クラス
記憶クラス
• auto, static, extern, register, typedef, volatile がある. – auto, static • 重要 – extern • 重要度高くない.分割コンパイル時に必要. – register • 重要度低い.高速化などに使用することあり. – typedef • 重要度はやや高い.「型に別名を付ける」のに使用. – volatile • 重要度低い.マルチスレッドプログラムなどで必要. C-24
auto変数,static変数
•auto変数は,ブロック内で誕生して,ブロックの 終わり(SCOPEの終わり)で消滅してしまう. – 変数が消滅すると,記憶されていた値も消える. – 毎回,初期化される. – 通常のローカル変数がこれにあたる. – スタック領域を使用. • auto int i; と,型の前に"auto"と書くと,auto変数となる. – ローカル変数は,明示的にstaticと書かない限り勝 手にauto変数となる. auto変数は 誕生と消滅を 繰り返す C-25auto変数,static変数
•static変数は,プログラム実行開始時に作成さ れ,消滅しない. – 値はプログラム終了まで保持される. – 初期化は,プログラム開始時に1回だけ行われる. – 全てのグローバル変数がこれにあたる. • ただし,スライド36も参照のこと. – ヒープ領域を使用. • static int i; と,型の前に"static"と書くと,static変数と なる.(例え,ローカル変数であっても) – グローバル変数は,強制的にstatic変数となる. C-26ヒープ領域 と スタック領域
• ヒープ領域とスタック領域の違い はサイズ – ヒープ領域にはメモリが大量にあり,巨大なメモリを使 用したいときはヒープ領域内に確保する – スタック領域には少量のメモリしかない.大量のメモリ をスタック領域内に確保するのは不可能 C-27auto変数とstatic変数の例
#include <stdio.h> void main(){ int i;for(i=0; i<3; i++){
printf("i=%d, ", i); { int j =6; printf("j=%d, ", j); j++; printf("j=%d¥n", j); } } } i=0, j=6, j=7 i=1, j=6, j=7 i=2, j=6, j=7 実行結果 int型変数jは, ブロックに入る毎に作成され,初期化され, ブロックを抜けて消滅してしまう. C-28
auto変数とstatic変数の例
#include <stdio.h> void main(){ int i;for(i=0; i<3; i++){
printf("i=%d, ", i); { static int j =6; printf("j=%d, ", j); j++; printf("j=%d¥n", j); } } } i=0, j=6, j=7 i=1, j=7, j=8 i=2, j=8, j=9 実行結果 int型変数jは, プログラム開始時に1回だけ作成され,1回だけ初期化され, プログラム終了まで消滅せず,値は保持される. C-29
auto変数と
static変数の例
#include <stdio.h> void main(){ int i;for(i=0; i<3; i++){
printf("i=%d, ", i); { static int j =6; printf("j=%d, ", j); j++; printf("j=%d¥n", j); } j = 100; /* j はここからは見えない! */ } } 例えstaticでも, jのスコープは, ブロックの中なので, ブロックの外からは アクセスできない. 感覚的には, 「存在しているが, 隠れていて見えない」 NG C-30
auto変数とstatic変数の例
#include <stdio.h> int x = 0; void count(){ printf("x=%d¥n", x); x++; } void main(){ count(); count(); count(); } x=0 x=1 x=2 実行結果 グローバル変数は, 強制的にstatic変数. 初期化は, プログラムの開始時に1回. 値はプログラム終了まで 保持される.auto変数とstatic変数の例
#include <stdio.h> void count(){ int x = 0; printf("x=%d¥n", x); x++; } void main(){ count(); count(); count(); } x=0 x=0 x=0 実行結果 失敗作. 関数に入るたびに, 変数xは作成され, x=0と初期化される. 関数から出るたびに 変数xは消滅する.auto変数とstatic変数の例
#include <stdio.h> void count(){ static int x = 0; printf("x=%d¥n", x); x++; } void main(){ count(); count(); count(); } 実行結果 x=0 x=1 x=2 static変数の初期化は, プログラム開始時に 1回だけ. 関数を抜けると, (見えなくなるが) 消滅せず, 値は保持される. C-33グローバル/ローカル
auto/static
• グローバル変数をautoにすることはできない. auto/staticを選べるのはローカル変数のみ. ローカル変数は(省略時は)自動的にauto変数 になる. • よって,「autoと積極的に記述すること」はほとん ど無い. • ローカル変数をstatic化するために,「static と積極的に記述すること」はある. C-34グローバル/ローカル
auto/static
• auto/staticは,記憶クラスを指定するもので あり,スコープを指定するものではない. ブロック内で変数宣言. 毎回初期化される. ブロックを抜けて値が消える. ブロック内からのみアクセス可能. 存在しない ブロック内で変数宣言. 初期は最初の1回のみ. ブロックを抜けても値が消えない. ブロック内からのみアクセス可能. 関数の外で変数宣言. 初期は最初の1回のみ. 値は消えない. 全ての関数からアクセス可能. 記憶クラス auto 記憶クラス static グローバル 変数 ローカル 変数 C-35グローバル/ローカル
auto/static
• グローバル変数は,強制的に「初期化は最初に1 回のみ,値は消えない」となる. • しかし,static付きグローバル変数と, staticなしグローバル変数は意味が異なる. – static付きグローバル変数は,同一ファイル 内からのみアクセス可能. – staticなしグローバル変数は,別ファイルか らもアクセス可能. • 分割コンパイル時に,この違いが意味を持つ. C-36typedef
• typedefは,既存の型に別名を付けるのに使用 typedef int seisuu;/*以後「seisuu型」が使える. int型の別名に過ぎない */ int i; seisuu j; C-37
typedefの例
typedef unsigned int uint;
– "unsigned int"に"uint"という別名が付いた. typedef long int int64, lint;
– "long int"に,"int64","lint"という2個の別 名が付いた.
typedef struct hoge sthoge;
– "struct hoge"に,"sthoge"という別名が付い た. C-38
おまけ:巨大なメモリの使用
• 以下のプログラムをコンパイル&実行したら,実 行時にエラーが発生し,強制終了された. #include <stdio.h> void main(){ int data[1000000]; printf("Hello, World!¥n"); } C-39おまけ:巨大なメモリの使用
• 以下のプログラムをコンパイル&実行したら,正 しく実行された. #include <stdio.h> int data[1000000]; void main(){ printf("Hello, World!¥n"); } C-40おまけ:巨大なメモリの使用
• 以下のプログラムをコンパイル&実行したら,正 しく実行された.
#include <stdio.h> void main(){
static int data[1000000]; printf("Hello, World!¥n"); } C-41