記事には広告を含む場合があります。

C言語入門講座

【C言語】変数のスコープとは?static・ローカル・グローバル変数の違い

おゆ

組み込み系プログラマとして5年働いていた元エンジニアです。得意言語はC言語とC++。本サイトでは学生および新人組み込み系プログラマに向けてプログラミング知識をわかりやすく解説しています。

C言語やC++を勉強しているとたまに出てくる変数のスコープって何?難しそうだからわかりやすく解説してほしい。
ローカル変数とグローバル変数とstatic変数って何が違うの?

こんな疑問にお答えします。

おゆ
C言語、C++の理解を深めるために変数のスコープ(有効範囲・寿命)の知識は必須です。この記事では具体例を使ってわかりやすく解説していきます。

変数のスコープとは変数の有効範囲(寿命)のこと

変数には有効範囲(寿命)があります。

変数の有効範囲は宣言の仕方によって異なります。

ローカル変数(詳しくは次の項目で解説)の有効範囲は一番内側のスコープ(中カッコ)の中だけです。

以下は変数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を呼び出しているからです。

スコープの外に出ると「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

おゆ
違う関数であれば変数名は被ってもOK。同じ関数内で変数名が被るのは基本的にNGだけど、変数の有効期限が切れた後に再び同じ名前の変数を作るのはOK。

ローカル変数=auto変数(自動変数)

ローカル変数は頭にautoをつけるのが正式な宣言方法です。

そのためローカル変数はauto変数または自動変数とも呼ばれます。

例5:

#include <stdio.h>
void main()
{
    auto int num=0; //autoなしでも同じ意味
    printf("num=%d\n",num);
}

実行結果

num=0

autoは省略可能なので通常は例1のようにautoなしで宣言します。

おゆ
下で紹介するstatic変数と明示的に区別したい場合は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変数なら値を保持したいという要求は満たしつつ、変数を宣言した関数内で書き換えられていないかだけを確認すればよいので圧倒的に管理が楽なんです。

おゆ
プログラミングはミスとの戦いです。プログラマにとっていかにミスをしにくいコードが書けるかは重要です。

ローカル変数・グローバル変数・static変数の初期値

ローカル変数は値を初期化しないと適当な値が代入されます。

一方でグローバル変数とstatic変数は初期化しないと自動的に0が代入されます。

しかしコードの読みやすさや予期せぬトラブル回避のためにも明示的に初期化しておくのをおすすめします。

まとめ

基本的にはローカル変数を使いますが、値を保持したい変数にはstatic変数を使います。

static変数では不便な場合は最終手段としてグローバル変数を使います。

ローカル変数は宣言された中カッコ内でのみ値が保持されますが、グローバル変数とstatic変数はプログラムの始めから終わりまで保持されます。

このように特徴が異なるのは前者と後者がメインメモリの別の場所に保存されるからです。

変数とメインメモリの関係について詳しく知りたい方は以下の記事をどうぞ。

プログラマとしてレベルアップするには必須の知識です。

変数とメインメモリの関係

スタック領域・ヒープ領域・静的領域の仕組みと違いをわかりやすく解説

プログラムを実行するとメインメモリはどうやって使われるの? スタック領域・ヒープ領域・静的領域ってどんな仕組み?それぞれどう違うの? こんな疑問にお答えします。 おゆプログラマーにとってメインメモリの ...

続きを見る

C言語で行き詰まったら…

C言語・C++がわからない時に質問できるサイト・サービス5選

C言語を独学で勉強しているけど内容がイマイチわからない。もはや何がわからないのかもわからない。 C++のエラーが解決できない。ググってもわからない。わかる人に解説してほしい。 こんなお悩みにお答えしま ...

続きを見る

ブログランキング参加中。クリックしてもらえると励みになります。

ブログランキング・にほんブログ村へ

-C言語入門講座