MMGamesロゴ  MMGames
Twitterシェアボタン  Facebookシェアボタン   
しんで覚えるC言語
しんで覚えるC言語

関数内で寿命が尽きる変数

ローカル変数の寿命
前章では関数について説明しましたが、関数と変数の関係は説明しませんでした。
ここでは、カウント関数を作り、それを元に関数と変数の関係を説明します。
カウント関数とは、呼び出された回数を数える関数のことです。
とくにそのような用語があるわけではありませんが、便宜上そのように呼びます。

単純に考えれば、それはかなり簡単に実現できます。
変数を用意して、それに毎回1足していけば良いだけのことです。
次のプログラムは、上記の考えをそのまま実現した例です。

ソースコード
#include <stdio.h>

int countfunc(void);

int main(void)
{
    countfunc();
    countfunc();
    countfunc();
    return 0;
}

int countfunc(void)
{
    int count = 0; /* 初期化 */
    count++;
    printf("%d\n", count);
    return count;
}

このプログラムの実行結果は、次の通りになります。

実行結果
1
1
1

変数countに1足す関数を3回呼び出したはずなのに、答えはすべて1です。
もしかしたら、初期化の部分で、0を代入していることが原因かもしれません。

そこで、あえて警告を無視して、変数を初期化せずに使ってみたいと思います。
この場合、初めにどんな数字が出るかわかりませんが、
それでも、2回目の呼び出しでは前回より1多く、3回目は2多く出るはずです。
次のプログラムは、変数countを初期化しない例です。

ソースコード
#include <stdio.h>

int countfunc(void);

int main(void)
{
    countfunc();
    countfunc();
    countfunc();
    return 0;
}

int countfunc(void)
{
    int count; /* 初期化なし(本当はやってはいけない) */
    count++;
    printf("%d\n", count);
    return count;
}

このプログラムの実行結果は、(筆者の環境では)次のようになりました。

実行結果
5369
5369
5369

2つ目、3つ目の数字共に、なぜかまったく同じ結果になってしまいました。
どうやら、1足した結果はまったく反映されていないようです。

これには、変数の寿命が深く関係しています。
実は、関数の中で宣言された変数は、関数が終了すると捨てられてしまうのです。
再び同じ関数が呼び出された場合、もう一度変数を作り直します。
この時、前回とは別のメモリが使われるので、値も変わってしまいます。

先ほどの場合、筆者の環境で実行させた時は偶然同じ値が出てきたようですが、
これも、結局の所まったく偶然の産物であり、同じ値が出てくるかはわかりません。

つまり、関数の中で宣言された変数の寿命は関数の中だけであるということです。
この様に、関数の中で寿命が終わる変数を、ローカル変数と呼びます。
また、変数の寿命や有効範囲などをまとめて、スコープと呼ぶことがあります。

キーワード
【ローカル変数】

関数の中で宣言された変数。仮引数の変数も同様。
その関数が終わると捨てられ、
再度関数が呼ばれた時には新しく作られる。
また、他の関数から使用することは出来ない。



キーワード
【スコープ】

識別子が有効な範囲を制限する仕組みの総称。
一般的には、変数の寿命と有効範囲を決定する仕組み。


同じ名前でも別の変数
ローカル変数は関数が終わると破棄されてしまうなら、
main関数で宣言しておけば、main関数が終わるまでは生き残るはずです。
次のプログラムは、main関数でもcount変数を宣言した例です。

ソースコード
#include <stdio.h>

int countfunc(void);

int main(void)
{
    int count = 0;
    countfunc();
    countfunc();
    countfunc();
    return 0;
}

int countfunc(void)
{
    int count; /* 初期化なし(本当はやってはいけない) */
    count++;
    printf("%d\n", count);
    return count;
}

このプログラムの実行結果は、(筆者の環境では)次のようになりました。

実行結果
5369
5369
5369

main関数でもcount変数を宣言したにもかかわらず、結果は変わりません。
しかも、main関数でcount変数を0にしているにもかかわらず、
countfunc関数内ではまったく別の値が表示されていることから考えると、
どうやら、main関数で宣言した変数は他の関数では使えないようです。

これは、変数の有効範囲が深く関係しています。
ローカル変数が有効なのは、宣言された関数の中のみです。
別の関数で同じ名前の変数が宣言されていても、それは別の変数となります。
したがって、main関数内の変数の値を変えても、他の関数の変数には影響がありません。
関数の独立性
ここまで、関数毎に、変数はまったく別々に作られていることがわかりました。
どうして、この様な仕組みになっているのかと言いますと、
その理由は、関数の独立性を高めるためなのです。

前章では、min~maxの合計を求めるsum関数を作りましたが、
この関数は、他のプログラムでもすぐに使えます。
この様に、関数を簡単に使えるようにするには、各関数が独立している必要があります。

たとえば、sum関数では、num変数を使用していました。
もしも、すべての関数で変数を共有するように作られていた場合は、
他の関数でnum変数を使っていた場合、その変数の値が変更されてしまいます。
したがって、sum関数を他のプログラムで使用する場合には、
事前に同じ名前の変数がないか、すべて調べておかなければなりません。

しかし、各関数で使われる変数が独立していれば、
他の関数の変数に影響を与える心配は一切ありません。
変数の値が突然変更されるという恐怖から開放されるのです。

プログラミング言語によっては
C言語もかなり古いプログラミング言語ですが、
もっと古い言語の中には、変数の寿命を持たない言語もあります。

そのような言語では、変数の値がどこかで勝手に変更されないよう、
プログラム全体に注意をはらわなければなりません。

その為、大規模なプログラムを記述する場合、
区別のために変数名がすごくたくさん必要になり、とても面倒でした

正確にはブロック内
これまで、ローカル変数の寿命を関数内と説明しましたが、正確にはブロック内です。
以前にも説明しましたように、ブロックとは、{}で囲まれている範囲のことを指しています。
これまでは、if文やfor文と一緒に使用してきましたが、
次のプログラムのように、それらの文に関係なく、関数内でブロックだけを使うことも可能です。

ソースコード
#include <stdio.h>

int main(void)
{
    int value1 = 10;
    int value2 = 20;
    
    printf("1:value1 %d\n", value1);
    printf("1:value2 %d\n", value2);

    {
        int value1;
        value1 = 30;
        value2 = 40;
        printf("2:value1 %d\n", value1);
        printf("2:value2 %d\n", value2);
    }

    printf("3:value1 %d\n", value1);
    printf("3:value2 %d\n", value2);

    return 0;
}

このプログラムの実行結果は、次の通りになります。

実行結果
1:value1 10
1:value2 20
2:value1 30
2:value2 40
3:value1 10
3:value2 40

注目部分は2つあります。
1つは、意図的に作った2つ目のブロックで、再びvalue1を宣言出来、
しかも、それが別の変数として扱われていることです。
2回目にvalue1が30だったのに、ブロックを抜けると10であることからわかります。

もう1つは、意図的に作ったブロックの中でも value2 が使えることです。
value2 はブロック中で変更した結果が、3回目の表示にも残っています。

if文やfor文の {} もブロックなので、同じ挙動になります。


本サイトについて

苦しんで覚えるC言語(苦C)は
C言語入門サイトの決定版です。
C言語の基本機能を体系立てて解説しており、
市販書籍と同等以上の完成度です。

第0部:プログラム概要編
  1. プログラムとは何か?
2章:プログラムの書き方
  1. 書き方のルール
  2. 書き方の慣習
  3. 練習問題2
3章:画面への表示
  1. 文字列の表示
  2. 改行文字
  3. 練習問題3
6章:キーボードからの入力
  1. 入力用の関数
  2. 入力の恐怖
  3. 練習問題6
9章:回数が決まっている繰り返し
  1. 繰り返しを行う文
  2. ループ動作の仕組み
  3. 練習問題9
10章:回数がわからない繰り返し
  1. 回数不明ループ
  2. 入力チェック
  3. 練習問題10
13章:複数の変数を一括して扱う
  1. 複数の変数をまとめて扱う
  2. 配列の使い方
  3. 練習問題13
20章:複数のソースファイル
  1. 最小限の分割
  2. 分割の定石
  3. 練習問題20

コメント
COMMENT

💬 コメント投稿欄を開く