こんな疑問にお答えします。
変数のスコープとは変数の有効範囲(寿命)のこと
変数には有効範囲(寿命)があります。
変数の有効範囲は宣言の仕方によって異なります。
ローカル変数(詳しくは次の項目で解説)の有効範囲は一番内側のスコープ(中カッコ)の中だけです。
以下は変数numの値を出力する簡単な例です。
例1:
#include <stdio.h> void main() { int num=0; //① printf("num=%d\n",num); }
実行結果
num=0
しかし①の行を中カッコでくくるとコンパイルエラーになります。
例2:
#include <stdio.h> void main() { {int num=0;} //① printf("num=%d\n",num); }
エラー
numという変数は宣言されていませんという意味のエラーです。
スコープの外に出ると「numはもう不要なんだ!」と判断されてnumは削除されてしまいます。
そのためprintf内でnumを使うように命令しても「numはもう無いよ!」と言われてしまうわけです。
ローカル変数
ローカル変数とは
中カッコ{ }の中で宣言された変数はローカル変数と呼ばれます。
ローカル変数の有効範囲は宣言が書かれた一番内側の中カッコ内のみで、中カッコの外に出ると使えなくなります。
例1:
#include <stdio.h> void main() { int num1=0; //ローカル変数 printf("num1=%d\n",num1); if(num1==0) { int num2=1; //ローカル変数 printf("num2=%d\n",num2); }//num2の有効範囲はここまで { char chara='A'; //ローカル変数 printf("chara=%c\n",chara); }//charaの有効範囲はここまで }//num1の有効範囲はここまで
実行結果
num1=0
num2=1
chara=A
変数には一般的にローカル変数を使います。
ローカル変数の利点はメモリを節約できる点です。
中カッコを抜けるたびにメモリが解放されるので、解放された分を別の変数のために再利用できます。
変数とメモリの関係については詳しくは以下の記事で解説しています。
変数とメインメモリの関係
-
スタック領域・ヒープ領域・静的領域の仕組みと違いをわかりやすく解説
プログラムを実行するとメインメモリはどうやって使われるの? スタック領域・ヒープ領域・静的領域ってどんな仕組み?それぞれどう違うの? こんな疑問にお答えします。 おゆプログラマーにとってメインメモリの ...
続きを見る
変数名の使い回し
ローカル変数のスコープ内では同じ名前の変数を定義できません。
以下のコードはコンパイルエラーになります。
例2:
#include <stdio.h> void main() { int num=0; int num=0; }
エラー文
しかしスコープの外に出て変数が削除された後なら同じ名前の変数を再び宣言できます。
例3:
#include <stdio.h> void main() { { int num=0; printf("num=%d\n",num); }//ここでnumは完全に削除される { int num=1; //上で宣言したnumとは全く別物 printf("new num=%d\n",num); }//ここでnumは完全に削除される }
実行結果
num=0
new num=1
2つ目のnumは1つ目のnumと同じ名前ですが、全く別の変数です。
異なる関数の中で宣言された変数名が被るのは問題ありません。
例4:
#include <stdio.h> void print_num(int num)//main関数のnumとは全く別物 { printf("new num=%d\n",num); } void main() { int num=0; //print_num関数のnumとは全く別物 printf("num=%d\n",num); print_num(1); }
実行結果
num=0
new num=1
ローカル変数=auto変数(自動変数)
ローカル変数は頭にautoをつけるのが正式な宣言方法です。
そのためローカル変数はauto変数または自動変数とも呼ばれます。
例5:
#include <stdio.h> void main() { auto int num=0; //autoなしでも同じ意味 printf("num=%d\n",num); }
実行結果
num=0
autoは省略可能なので通常は例1のようにautoなしで宣言します。
グローバル変数
グローバル変数とは
グローバル変数とは中カッコの外で宣言された変数のことです。
グローバル変数は中カッコの外からでも呼び出し可能で、有効期限(スコープ)はプログラムの始まりから終わりまでです。
以下の例ではグローバル変数numの値がずっと保持されていることがわかります。
例1:
#include <stdio.h> int num=0; //グローバル変数 void print_num(void); void main() { print_num(); print_num(); print_num(); printf("final num=%d\n",num); } void print_num(void) { printf("num=%d\n",num); num=num+1; }
実行結果
num=0
num=1
num=2
final num=3
通常はグローバル変数の書き換えはほとんどしません。値がわからなくなると困るからです。上はあくまでグローバル変数の特徴を知ってもらうための例です。
ほとんどの場合はローカル変数だけでも事足ります。
一方でプログラムを通して何度も使う値はいちいち宣言するのが手間ですし、変数の値を変えたい場合に修正漏れが発生する恐れがあります。
そういう時にはグローバル変数が便利です。
ただしグローバル変数の乱用には注意です。
数十、数百のファイルから成るプログラムのあちこちでグローバル変数が宣言されている場合を考えてみてください。
どの変数名が既に使われているのか、グローバル変数の値はいくつなのかを調べるのが大変なのが想像できると思います。
そのため通常はローカル変数を利用し、どうしても必要な変数のみをグローバル変数にします。
グローバル変数は1つのヘッダーファイルにまとめるなど工夫すると管理しやすいです。
同じ名前のグローバル変数とローカル変数がある場合
以下の例ではグローバル変数とローカル変数それぞれで同じnumという名前の変数を宣言しています。
例2:
#include <stdio.h> int num=0; //グローバル変数 void main() { { int num=1; //ローカル変数 printf("①num=%d\n",num); //ローカル変数のnumの値が出力される } //ローカル変数のnumはここで削除 printf("②num=%d\n",num); //グローバル変数のnumの値が出力される }
実行結果
①num=1
②num=0
実行結果を見ると、①ではローカル変数の値が出力されています。
ローカル変数のスコープ内ではローカル変数が優先されるからです。
スコープを出るとローカル変数のnumは削除されるので、②ではグローバル変数の値が出力されます。
このように同じ変数名のグローバル変数とローカル変数が存在した場合はローカル変数が優先されます。
static変数(静的変数)
static変数とは
ローカル変数とグローバル変数ほどは使いませんが、static(スタティック)変数もあります。
頭にstaticと付けて変数を宣言するとstatic変数になります。
static変数もグローバル変数のようにプログラムの終わりまで値が保持されるのが特徴です。
しかしグローバル変数とは違って使えるのは宣言した関数の中だけです。
以下の例ではprint_num関数の中でstatic変数を宣言しています。
例1:
#include <stdio.h> void print_num(void); void main() { print_num(); print_num(); print_num(); } void print_num(void) { static int num=0; //static変数の初期化は最初だけ行われる printf("num=%d\n",num); num=num+1; }
実行結果
num=0
num=1
num=2
static変数の初期化は一度だけ行われ、関数を出た後も値は保持されます。
よって例ではprint_num関数を呼び出すたびにnumの値が増えています。
static変数は宣言された関数内でのみ有効なので、main関数から呼び出すとコンパイルエラーになります。
比較としてnumをローカル変数にした場合の結果も載せておきます。
numの宣言以外は同じコードですが、ローカル変数は関数を出るとリセットされるので実行結果は異なります。
例2:
#include <stdio.h> void print_num(void); void main() { print_num(); print_num(); print_num(); } void print_num(void) { int num=0; //ローカル変数 printf("num=%d\n",num); num=num+1; }
実行結果
num=0
num=0
num=0
static変数はなぜ必要か
確かに短いコードであれば変数の管理がしやすいので、値を保持したいならグローバル変数を使っておけば事足ります。
しかし実務で使うコードは何百、何千行にも及びます。
グローバル変数だとどこからでも書き換えられてしまうので、いちいち書き換えがないかや現在の値はいくつなのかを考えなくてはいけません。
複数のファイルから成るプログラムの場合、他のファイルで同じ変数名が使われていないかの確認も行う必要があります。
これでは面倒ですしミスが起こりやすいです。
static変数なら値を保持したいという要求は満たしつつ、変数を宣言した関数内で書き換えられていないかだけを確認すればよいので圧倒的に管理が楽なんです。
ローカル変数・グローバル変数・static変数の初期値
ローカル変数は値を初期化しないと適当な値が代入されます。
一方でグローバル変数とstatic変数は初期化しないと自動的に0が代入されます。
しかしコードの読みやすさや予期せぬトラブル回避のためにも明示的に初期化しておくのをおすすめします。
まとめ
基本的にはローカル変数を使いますが、値を保持したい変数にはstatic変数を使います。
static変数では不便な場合は最終手段としてグローバル変数を使います。
ローカル変数は宣言された中カッコ内でのみ値が保持されますが、グローバル変数とstatic変数はプログラムの始めから終わりまで保持されます。
このように特徴が異なるのは前者と後者がメインメモリの別の場所に保存されるからです。
変数とメインメモリの関係について詳しく知りたい方は以下の記事をどうぞ。
プログラマとしてレベルアップするには必須の知識です。
変数とメインメモリの関係
-
スタック領域・ヒープ領域・静的領域の仕組みと違いをわかりやすく解説
プログラムを実行するとメインメモリはどうやって使われるの? スタック領域・ヒープ領域・静的領域ってどんな仕組み?それぞれどう違うの? こんな疑問にお答えします。 おゆプログラマーにとってメインメモリの ...
続きを見る
C言語で行き詰まったら…
-
C言語・C++がわからない時に質問できるサイト・サービス5選
C言語を独学で勉強しているけど内容がイマイチわからない。もはや何がわからないのかもわからない。 C++のエラーが解決できない。ググってもわからない。わかる人に解説してほしい。 こんなお悩みにお答えしま ...
続きを見る