5-12 ローカル変数とグローバル変数

【練習問題】

足し算だけができる簡単な電卓を作ってみましょう。

エディットに適当な数を入力して

+」ボタンを押すと画面には現れない場所 (変数) に値が保存され、エディットは空欄になり、

その後、エディットに別な数を入力して「=」ボタンを押すと

和が求まる、というプログラムです。

まずは、フォームにコンポーネントを配置しましょう。

コンポーネント Name
左のエディット EditNumber
右上のボタン ButtonPlus
右下のボタン ButtonEqual

このプログラムでは、整数型変数 PrevNumber を用意して、「+」ボタンが押されたらそこに値を格納するようにします (下のプログラムを参照)。

まず、「+」ボタンのイベントハンドラを書きましょう。

procedure TForm1.ButtonPlusClick(Sender: TObject);
var
  PrevNumber: Integer; {ここで PrevNumber を用意}
begin
  PrevNumber := StrToInt(EditNumber.Text); {値を格納する}
  EditNumber.Text := ''; {エディットを空欄にする}
end;

次に、「=」ボタンのイベントハンドラを書きましょう。

procedure TForm1.ButtonEqualClick(Sender: TObject);
begin
  EditNumber.Text := IntToStr(PrevNumber
    + StrToInt(EditNumber.Text));
end;

実行してみましょう。 すると…

「未定義の識別子 : 'PrevNumber'」というエラーが出ましたね。これは、「'PrevNumber' という変数 (か何か) を使いたいのだろうけど、そんなものは知らないよ」というエラーです。このエラーをダブルクリックすると、

エラーが発生した行が茶色で、エラーが発生した場所 (Delphi が解釈できなくなった場所) が水色で示されます。

PrevNumber は宣言したはずなのに、未定義エラーが出たのはなぜでしょうか?

実は、Delphi (PASCAL 言語) には、「モジュールの中で用意された変数は、そのモジュールの中だけで有効である」という決まりがあります。具体的に示すと次のようになります。

では、どうしたらよいのでしょう? この場合は、どのモジュールからでも利用できるように、PrevNumber を用意しましょう。コードエディタのずっと上のほうを見て、「type」で始まる次の部分を探してください。

この end; の直前で変数を宣言します。下線部を付け加えてください。(この「type」の中で宣言する場合、var」はいりません。)

type
  TForm1 = class(TForm)
  ButtonEqual: TButton;
  EditNumber: TEdit;
  procedure ButtonPlusClick(Sender: TObject);
  procedure ButtonEqualClick(Sender: TObject);
private
  { Private 宣言 }
public
  { Public 宣言 }
  PrevNumber: Integer;
end;

また、これで先ほどの変数はいらなくなりました。下線部を削除してください。

procedure TForm1.ButtonPlusClick(Sender: TObject);
var
  PrevNumber: Integer; {ここで PrevNumber を用意}
begin
  PrevNumber := StrToInt(EditNumber.Text); {値を格納する}
  EditNumber.Text := ''; {エディットを空欄にする}
end;

実行してみましょう。 今度はちゃんと動作するはずです。

このようにどのモジュールからも利用することができる変数を、「グローバル変数 (大域変数) 」と呼びます。一方、モジュールの中で宣言された変数は「ローカル変数(局所変数)」 と呼ばれます。変数の有効範囲をスコープと言います。

コラム

「それなら、どんな変数もグローバル変数にするほうが便利なのでは?」と考える人もいるかもしれません。すべての変数をグローバル変数にするとどうなるでしょう?

  1. 大域変数の「どのモジュールからも利用できる」という性質は、言い換えれば「どのモジュールからでもその値を変更 (破壊)することができる」ということです。これはつまり、変数の値がおかしくてプログラムが正しく動かなかったりエラーが発生したりした場合にはすべてのモジュールを疑わなければならないことを意味しています。逆にローカル変数であれば、他のモジュールからは手が出せないことが保証されているので、問題があるモジュールだけを調べればよいのです。
  2. すべての変数をグローバル変数にすると、変数の名前をたくさん用意しなければなりません。しかし、ローカル変数はそのモジュール内だけで有効ですから、下の例のように、同じ名前の変数を複数のモジュールで使うことができます。

※プログラミングでは、複数のモジュールで共有する必要のない限りは「ローカル変数」を使うのが鉄則とされています。