全460ページの「エクセル(Excel)大事典」を読めば“演算誤差の謎”がよくわかる!
◆エクセルVBA講座「変数のすべて」
第5回 変数の適用範囲(スコープ)
変数には適用範囲があります。適用範囲とは「Aというエリアで宣言した変数は、Bというエリアでは使えない」など「その変数はどこで使えるか」といった考え方(仕組み)です。この"変数の適用範囲"という考え方を理解するには、まずVBAにはどんな"エリア"があるかを理解しなければなりません。
マクロの記述エリア「モジュール」
マクロとは、Excelを自動実行させるための"命令書"です。その命令を記述できる場所のことを「モジュール」と呼びます。マクロはブック内に保存されますので、モジュールもブックごとに管理されています。エクセルVBAで利用できる一般的なモジュールは次の通りです。
・シートモジュール
シートごとに別々のモジュールが存在します。一般的に、ワークシート[Sheet1]のモジュール名は[Sheet1]になりますが、シート見出しに表示されるワークシート名とモジュール名は必ずしも一致しません。グラフシートにもモジュールがありますが、よく使われるのはワークシートのモジュールです。
・ブックモジュール
ブックに最初から用意されているモジュールです。ブックを開いたとき自動的に実行するマクロなどは、このブックモジュールに記述します。プロジェクトエクスプローラでは[ThisWorkbook]と表示されています。
・フォームモジュール
UserForm(フォーム)のモジュールです。UserFormを操作するマクロはここに記述します。UserFormを挿入すると使用できるようになります。
・標準モジュール
ワークシートやブックに属さない汎用的なモジュールです。VBEで[挿入]-[標準モジュール]を実行すると使用できるようになります。また、マクロ記録を実行すると自動的に標準モジュールが挿入されます。
エクセルVBAでは他にもクラスモジュールというモジュールがありますが、ここでは割愛します。クラスモジュールは、ユーザーが独自のクラスを定義するときに使用するモジュールです。
それぞれのモジュールは上図のようなイメージです。
1つのブックには、マクロを記述する場所(モジュール)が複数あるということだけ理解してください。
マクロの最小単位「プロシージャ」
1つの命令書(モジュール)には、複数の命令(マクロ)を記述できます。実行されるマクロは「Sub マクロ名」から始まり「End Sub」で終わります。このように、実行されるマクロの最小単位を「プロシージャ」と呼びます。
プロシージャには、Subで始まるSubプロシージャと、Functionで始まるFunctionプロシージャがありますが、ここではSubプロシージャを例に解説します。
変数の適用範囲(プロシージャ間)
まず、プロシージャ間での適用範囲について解説します。
プロシージャの内部で宣言した変数は、宣言したプロシージャの中でしか使用できません
Dim buf As String ''文字列型の変数を宣言する
buf = "tanaka" ''変数に"tanaka"という文字を入れる
End Sub
Sub Sample2()
MsgBox buf ''変数の値を表示する(←エラーになる)
End Sub
上のコードは、プロシージャ「Sample1」の中で変数bufを宣言しています。変数bufには"tanaka"という文字列を入れていますが、この変数bufを別のプロシージャ「Sample2」で参照することはできません。もちろん、変数内を参照できないだけでなく、次のように値を入れようとしてもエラーになります。
Dim buf As String ''文字列型の変数を宣言する
End Sub
Sub Sample2()
buf = "tanaka" ''変数に"tanaka"という文字を入れる(←エラーになる)
MsgBox buf ''変数の値を表示する
End Sub
プロシージャの中で宣言した変数は、宣言したプロシージャの中でしか使用できません。このような変数を「ローカル変数」と呼びます。
しかし、ときには複数のプロシージャで同じ変数を使いたいこともあります。1つのモジュール内にあるすべてのプロシージャで同じ変数を使えるようにするには、モジュール内の「宣言セクション」で変数を宣言します。宣言セクションとは、モジュールの先頭から、最初のプロシージャまでの間です。
Sub Sample1()
buf = "tanaka" ''変数に"tanaka"という文字を入れる
Call Sample2 ''プロシージャ「Sample2」を実行する
End Sub
Sub Sample2()
MsgBox buf ''変数の値を表示する
End Sub

宣言セクションで宣言した変数は、宣言したモジュール内のすべてのプロシージャで使用できます。このような変数を「モジュールレベル変数」と呼びます。
変数の適用範囲(モジュール間)
上記は、1つのモジュール内での適用範囲について解説しました。今度は、複数のモジュール間で変数の適用範囲を考えてみましょう。
ここでは、標準モジュール「Module1」と、標準モジュール「Module2」を例にします。まず、モジュールの宣言セクションで宣言する「モジュールレベル変数」が他のモジュールで使用できるかどうかを検証してみましょう。Module1とModule2に、それぞれ次のようなコードを書きます。
Module1
Sub Sample1()
buf = "tanaka" ''変数に"tanaka"という文字を入れる
Call Sample2 ''プロシージャ「Sample2」を実行する
End Sub
Module2
MsgBox buf ''変数の値を表示する
End Sub
プロシージャ「Sample1」を実行すると、Module2のプロシージャ「Sample2」でエラーになります。
予想通り、モジュールレベル変数は、他のモジュールで使用できません。まず、この点を理解してください。では、すべてのモジュールで使用できる変数を宣言するときは、どうしたらいいのでしょう。それには「Public」という命令を使って変数を宣言します。
Module1
Sub Sample1()
buf = "tanaka" ''変数に"tanaka"という文字を入れる
Call Sample2 ''プロシージャ「Sample2」を実行する
End Sub
Module2
MsgBox buf ''変数の値を表示する
End Sub
今度は、Module1で宣言した変数bufを、Module2で使用できました。このように、すべてのモジュールで使用できる変数を「パブリック変数」と呼びます。
以上の特性を整理すると、下図のようになります。それぞれの変数は、宣言の違いにより矢印で結んだ場所で使用可能です。
変数は適用範囲を考えて宣言する
変数の適用範囲をまとめてみましょう。
・ローカル変数
プロシージャの内部で、Dimを使って宣言します。宣言したプロシージャだけで使えます
・モジュールレベル変数
モジュールの宣言セクションで、Dimを使って宣言します。宣言したモジュール内のすべてのプロシージャで使えます
・パブリック変数
モジュールの宣言セクションで、Publicを使って宣言します。すべてのモジュールの、すべてのプロシージャで使えます
もっとも広範囲で使える変数はパブリック変数です。複数のモジュールで構成される大規模なマクロでは必須です。モジュールがひとつしか存在しない小規模なマクロでは、どのプロシージャで何の変数を使うかを検討して、必要な変数だけをモジュールレベル変数として宣言します。ときどき次のような宣言を見かけますが、適切な使い方ではありません。
Sub Sample1()
For i = 1 To 10
If Cells(i, 1) = "tanaka" Then buf = Cells(i, 1).Address
Next i
MsgBox buf
End Sub
Sub Sample2()
For i = 1 To Worksheets.Count
If Worksheets(i).Name = "合計" Then buf = Worksheets(i).Range("A1")
Next i
MsgBox buf
End Sub
Sample1とSample2では、どちらも変数iと変数bufを使っていますが、両者はまったく別の処理をしています。2つのプロシージャで同じような変数を使うからといって、これらの変数をモジュールレベル変数として宣言するのは間違っています。Sample1の変数iと変数bufはSample1内だけで完結し、Sample2で使用されることはありません。この場合、変数iと変数bufをモジュールレベルにする必要性がありません。次のように、それぞれプライベート変数として宣言しましょう。
Dim i As Long, buf As String
For i = 1 To 10
If Cells(i, 1) = "tanaka" Then buf = Cells(i, 1).Address
Next i
MsgBox buf
End Sub
Sub Sample2()
Dim i As Long, buf As String
For i = 1 To Worksheets.Count
If Worksheets(i).Name = "合計" Then buf = Worksheets(i).Range("A1")
Next i
MsgBox buf
End Sub
モジュールレベル変数やパブリック変数は、手抜きをするために使う機能ではありません。
また、ローカル変数は、Subプロシージャが終了すると値がクリアされますが、モジュールレベル変数とパブリック変数はSubプロシージャが終了しても値がクリアされず、次に別のプロシージャを実行したときにも前の値を保持しています。こうした特性を正しく認識していないと、予期せぬバグを招く原因になりかねません。
別のプロシージャで値を使用する方法
モジュールレベル変数は、複数の異なるプロシージャ間で変数の値を共有したいときに使用します。しかし、別のプロシージャに値を引き渡すだけならモジュールレベル変数を使わなくても可能です。
Dim buf As String ''文字列型の変数を宣言する
buf = "tanaka" ''変数に文字列"tanaka"を入れる
Call Sample2(buf) ''Sample2の引数として変数bufを引き渡す
End Sub
Sub Sample2(tmp As String) ''Sample2は1つの文字列を引数として受け取る
MsgBox tmp ''受け取った文字列を表示する
End Sub
値を引き渡す先のプロシージャ(Sample2)は、引数として1つの文字列を受け取るようにします。こうした使い方は、変数の機能というよりも、SubプロシージャやFunctionプロシージャの特性です。本当は、受け取った値を編集するケースに応じて「値渡し」や「参照渡し」などのテクニックが存在するのですが、そうした話はまた別の機会に詳解しましょう。





