変数のスコープ

変数のスコープとは

変数のスコープとは、その変数を参照することの出来る範囲のことです。スコープに応じて、それぞれ変数の呼び方が異なります。

ローカル変数

ローカル変数*1 とは、関数(手続き)の内部で宣言された変数のことを指します。サンプルをいくつかあげます。

procedure MyProc;
var
  Num: Integer; // ローカル変数 Num を宣言
  s: string;    // ローカル変数 s を宣言
begin
  //...
end;

function MyFunc: Integer;
var
  Value: Integer; // ローカル変数 Value を宣言
begin
  //...
end;

上の MyProc( ) 内で宣言された変数 Num, s および MyFunc( ) 内で宣言された変数 Value のことをローカル変数と呼びます。

ローカル変数は、一番スコープが狭いです。ローカル変数は、それが宣言された関数(手続き)、イベントハンドラ内でのみ参照できます。

procedure SayHello;
var
  Greeting: string; // ローカル変数 Greeting を宣言
begin
  Greeting := 'Hello'; // ローカル変数に値を代入する

  ShowMessage(Greeting); // ローカル変数に格納されている値を表示する
end;

手続き SayHello( ) では、ローカル変数 Greeting を宣言し、手続き内で値の代入、表示を行っています。このローカル変数 Greeting は、SayHello( ) 内でのみ参照することが出来ます。つまり、変数 Greeting のスコープは SayHello( ) 内のみ、ということですね。

ここで間違ったサンプルを示すことで、「ローカル変数とはどういったものなのか」を理解するのに助けになるかもしれません。結局、ローカル変数とはその名の通り'ローカル'な変数という意味で、外部(つまり、それが宣言された関数の外)からは参照できませんよ、ということだけです。

procedure DisplayGreeting;
begin
  // SetGreeting 関数内で宣言されている
  // ローカル変数 Greeting を参照する. これは出来ない!!
  ShowMessage(Greeting); 
end;

procedure SetGreeting(s: string);
var
  Greeting: string;
begin
  Greeting := s;
end;

procedure SayHello;
begin
  SetGreeting('Hello');
  DisplayGreeting;
end;

上のプログラムをコンパイルすると、「未定義の識別子: 'Greeting'」とコンパイルエラーが表示されます。これはコンパイラが「手続き DisplayGreeting で Greeting というもの(識別子)を参照してるけど、そんなものは見当たらないよ」と文句を言っているわけです。プログラマにしてみれば、(意図としては)SetGreeting で宣言している Greeting を参照したいと考えているのかもしれませんが、コンパイラには、そんなことはわかりません。なぜなら、SetGreeting( ) で宣言された変数 Greeting のスコープは、SetGreeting( ) 内のみですので、それ以外の関数(手続き)から、つまり DisplayGreeting( ) からは見えないのですから。

関数の内部で宣言された変数は、その関数内でしか参照できません。ですから、2 つの異なる関数の内部で同じ名前の変数が宣言されたとしても、それらは全く異なったものとなります。

procedure MyProc1;
var
  Num: Integer;
begin
  Num := 1200;
  ShowMessage(IntToStr(Num));
end;

procedure MyProc2;
var
  Num: Integer;
begin
  Num := 30;
  ShowMessage(IntToStr(Num));
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  MyProc1;
  MyProc2;
end;

MyProc1( ) を呼び出すと "1200" と表示され、MyProc2( ) を呼び出すと "30" と表示されます。MyProc1( ) と MyProc2( ) で宣言されている Integer 型の変数は、どちらも Num という名前が付いていますが、これらは全く異なった変数です。共通点は名前が同じということだけで、MyProc1( ) で変数 Num に値 1200 を代入したからといって、MyProc2( ) で宣言されている変数 Num にも1200 が代入されるということはありません。

以上のことをまとめると次のようになります。

これは、次のように言い換える事も出来ます。

グローバル変数(ユニットグローバル)

グローバル変数は、ローカル変数よりも大きなスコープを持ちます。例えば、次のプログラムではグローバル変数 Book をそれぞれ DisplayBook1, DisplayBook2, DisplayBook3 の 3 つの手続きから参照しています。

implementation

{$R *.dfm}

var
  Book: string = 'Delphi プログラミングバイブル'; // グローバル変数 Book

procedure DisplayBook1;
begin
  ShowMessage(Book);
end;

procedure DisplayBook2;
begin
  ShowMessage(Book);
end;

procedure DisplayBook3;
begin
  ShowMessage(Book);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  DisplayBook1;
  DisplayBook2;
  DisplayBook3;
end;

ローカル変数は、それが宣言された関数(手続き)の中でしか参照できませんでしたが、グローバル変数ではスコープが大きくなり、参照できる箇所が広くなります。また、プログラムからも分かりますように、グローバル変数は関数の外側で宣言します。

上のサンプルプログラムのようにグローバル変数には初期値を設定できます(ローカル変数では出来ません)。初期値の設定はオプションです。初期値を省略した場合、 グローバル変数は、それが string であればカラに、Integer であれば 0 に、ポインタであれば、nil に初期化されます。

この節で取り上げたいことは、「グローバル変数 Book は *implementation 部で宣言されている」という点です。implementaion 部で宣言されたグローバル変数は、ユニットグローバルな変数です。ユニットグローバルとは、そのグローバル変数のスコープが(その変数が宣言された)ユニット内に限定されているという意味です。

まとめると次のようになります。

ある変数が複数のユニットから参照される必要があるなら、「真のグローバル変数」を使用します。

グローバル変数(真のグローバル)

まず断っておきたいのですが、「真のグローバル」という言葉は、筆者が勝手につけた名前です。ユニットグローバルとの違いを強調する為に、ここではあえて「真のグローバル変数」と呼ぶことにします。大事なのは呼び方ではなく、ユニットグローバルな変数とどこが違うのかという点を学ぶことです。

真のグローバル変数は、どこからでも参照できる一番スコープの広い変数です。ユニットグローバルとの違いは、真のグローバル変数が宣言されてあるユニット以外のユニットからも参照できるかどうかという点です。*2

真のグローバル変数は、ユニットの interface 部で宣言します。

Unit2

unit Unit2;

interface

var
  Greeting: string = 'Hello';

implementation

end.

Unit1

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Unit2;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(Greeting);
end;

end.

まとめると次のようになります。

まとめ

補足

細かいことですが、「implementation 部で宣言されたグローバル変数は、そのユニット内から参照することが出来る」という表現は、若干ですが正しくはありません。それは、次のプログラムで確認できます。

implementation

{$R *.dfm}

procedure SayGreetting;
begin
  ShowMessage(Greeting); // エラー!! 未定義の識別子: Greeting
end;

var
  Greeting: string = 'Hello';

より正確には、「implementation 部で宣言されたグローバル変数は、そのグローバル変数が宣言された行よりも後の行以降でアクセスできる」となります。


*1 局所変数とも呼ばれます。

*2 ただし、参照したい真のグローバル変数が宣言してあるユニットを、参照する側の uses 節に指定する必要があります。uses 節については、uses 節、interface 部、implementation 部を参照してください。


up next
Last update 2003/12/08