このページの内容について
このページは、VBAエキスパート(ExcelVBAスタンダード)試験合格講座の第一回記事です。公式テキスト第1章「プロシージャ」と第2章「変数」の内容について解説します。自分でも手を動かしながら読み進めてください。1時間の講義でお話する程度の分量です。主に講座受講者の復習での利用を想定しています。
プロシージャの呼び出し
ベーシックまでの内容
プロシージャはマクロの最小実行単位です。プロシージャには値を返すことができないSubプロシージャと値を返すことができるFunctionプロシージャがあります。これだけ見ると値を返すことができるFunctionプロシージャのみを使えば良いように見えますが、呼び出し方やユーザーからの見え方に違いがあり、開発においてはそれぞれ使い分けて使用します。プロシージャはプロシージャを呼び出すことができます。
サンプルコード(クリックでコピー)
Private Sub Caller()
Call Msgbox(Adder(10, 11))
End Sub
Private Function Adder(ByVal x As Long, ByVal y As Long) As Long
Adder = x + y
End Function
実行結果
上記サンプルコード上段の「Caller」を実行すると、Functionプロシージャである「Adder」を呼び出します。「Adder」は2つの整数(Long)を受け取り、足し算した結果を返します。「Caller」は返された整数値「21」をそのままMsgbox関数に渡してメッセージを表示します。
値渡しと参照渡し
プロシージャは値を受け取ることができます。値の受け取り方には2種類あります。
値渡し
引数に「ByVal」を設定すると、値渡しになります。値渡しされた引数はプロシージャ内で書き換えをしても呼び出し元に影響しません。
参照渡し
引数に「ByRef」を設定すると、参照渡しになります。参照渡しされた引数はプロシージャ内で書き換えた内容が、呼び出し元に反映します。
サンプルコード(クリックでコピー)
Private Sub RefCaller()
Dim a As Long, b As Long
a = 10
b = 11
Call ReferenceTest(a, b)
Debug.Print a
Debug.Print b
End Sub
Private Sub ReferenceTest(ByRef x As Long, ByVal y As Long)
x = x + 10
y = y + 10
End Function
実行結果
上記サンプルコード上段の「RefCaller」を実行すると「ReferenceTest」を呼び出します。「ReferenceTest」は第一引数を参照渡しで受け取り、第二引数を値渡しで受け取ります。そして2つの整数にそれぞれ「10」を足して処理を終了します。「RefCaller」は「ReferenceTest」に渡した2つの変数をそれぞれイミディエイトウィンドウに出力します。第一引数として渡した変数「a」には10が加算されており、第二引数として渡した変数「b」には呼び出し先の変更が反映されていないことを確認できます。
配列
変数と配列の違い
これまで学習してきた「変数」には一つのデータのみを格納することができました。このため、複数のデータを扱う場合、変数それぞれに名前つけてデータの数だけ変数を用意する必要がありました。配列は同じ型の複数の変数を連ねて一つの名前で管理することができるものです(下図)。
変数のイメージ

データひとつにつき1つの変数が必要であり、変数名の管理が大変・・。
配列のイメージ

1つの名前と番地(インデックス)で管理できる。表のように扱うことができる。
要素とインデックス(添え字)
配列は変数と同様、任意の名前を付けて宣言することで使用できます。配列に入っているデータひとつひとつを「要素」、データにアクセスするための数値を「インデックス」もしくは「添え字」と呼びます。
配列の種類(宣言方法による違い)
宣言の方法の違いで2種類の配列を使い分けるができます。
静的配列
宣言時に配列の大きさを指定し、マクロの実行中に配列の要素数を変更できない配列を静的配列(せいてきはいれつ)と呼びます。
宣言の方法
基本は変数と同じで、要素数は、変数名の後ろに「1 to 5」のように指定します。
サンプルコード(クリックでコピー)
Private Sub dim_static_arr()
Dim arr_1(0 to 2, 0 to 3) As String
Dim arr_2(1 to 9, 1 to 2) As Long
Stop
End Sub
実行結果
上記サンプルコードを実行すると「Stop」で動作が停止するため、ローカルウィンドウで中身を見てみましょう。「arr_1」と「arr_2」いう静的配列を宣言しています。

動的配列
宣言時に配列の大きさを指定せず、マクロの実行中に配列の要素数を書き換えることができる配列を動的配列(どうてきはいれつ)と呼びます。
宣言の方法
静的配列と異なり、宣言時には「0 To 3」のような要素数を指定しません。
サンプルコード(クリックでコピー)
Private Sub dim_dynamic_arr()
Dim arr_1() As String
Stop
End Sub
実行結果
上記サンプルコードを実行すると「Stop」で動作が停止するため、ローカルウィンドウで中身を見てみましょう。「arr_1」という動的配列を宣言していますが、要素数が決まっていないため中身のない配列となっています。

ReDim(動的配列の再定義)
動的配列の要素数を決めるには配列の再定義(ReDim)をします。
サンプルコード(クリックでコピー)
Private Sub ReDim_dynamic_arr()
Dim arr_1() As String
ReDim arr_1(1 To 3, 1 To 2)
Stop
End Sub
実行結果
上記サンプルコードを実行すると「Stop」で動作が停止するため、ローカルウィンドウで中身を見てみましょう。「arr_1」という動的配列を再定義しており、6(3×2)個のデータが格納できる配列になっています。

ReDimで再定義済の配列を再定義する
動的配列はマクロの実行中に何度でもReDimで再定義することができます。しかし、ReDimは配列に入力済のデータを初期化してしまいます。
サンプルコード(クリックでコピー)
Private Sub ReDim_dynamic_arr()
Dim arr_1() As String
ReDim arr_1(1 To 3, 1 To 2)
arr_1(1, 1) = "F0000001"
arr_1(2, 1) = "F0000002"
arr_1(3, 1) = "F0000003"
arr_1(1, 2) = "ユーザーフォームに最小化/最大化ボタンを表示させる"
arr_1(2, 2) = "文字列に含まれる特定のワードの出現回数を返す"
arr_1(3, 2) = "マクロ実行者のデスクトップパスを取得する"
Debug.Print arr_1(3, 2)
ReDim arr_1(1 To 3, 1 To 3)
arr_1(1, 3) = "2024/07/28"
arr_1(2, 3) = "2024/07/30"
arr_1(3, 3) = "2024/07/31"
Debug.Print arr_1(1, 1) & ":" & arr_1(1, 3)
End Sub
実行結果
上記サンプルコードでは、3×2の大きさの配列をReDimで再定義しています。
配列には、
1列目 ⇒ 関数のID
2列目 ⇒ 関数の名前
を代入し、入力確認のため3行・2列目の値をイミディエイトウィンドウに出力します。
ここで配列にデータを1列追加したくなり、ReDimで列の値を一つ増やして再定義しました。追加した3列目に公開日を代入し、同じように入力確認をすると先に入力していた内容が反映されませんでした。
入力済のデータを保持してReDimする
入力済のデータを保持したまま、ReDimで動的配列を再定義するには「Preserve」キーワードを使用します。
サンプルコード(クリックでコピー)
Private Sub ReDim_Preserve_dynamic_arr()
Dim arr_1() As String
ReDim arr_1(1 To 3, 1 To 2)
arr_1(1, 1) = "F0000001"
arr_1(2, 1) = "F0000002"
arr_1(3, 1) = "F0000003"
arr_1(1, 2) = "ユーザーフォームに最小化/最大化ボタンを表示させる"
arr_1(2, 2) = "文字列に含まれる特定のワードの出現回数を返す"
arr_1(3, 2) = "マクロ実行者のデスクトップパスを取得する"
Debug.Print arr_1(3, 2)
ReDim Preserve arr_1(1 To 3, 1 To 3)
arr_1(1, 3) = "2024/07/28"
arr_1(2, 3) = "2024/07/30"
arr_1(3, 3) = "2024/07/31"
Debug.Print arr_1(1, 1) & ":" & arr_1(1, 3)
End Sub
実行結果
前述のサンプルコードとの違いはReDimするときに「Preserve」を指定しているか否かです。今回はPreserveを指定しているため、再定義後も先に入力していたデータが保持されます。
配列の種類(次元数による違い)
配列には「次元」という概念があります。ここまでサンプルで取り上げてきた配列はすべて「2次元配列」です。2次元配列は要素を「,(カンマ)」で区切って宣言し、データを入れ子構造で格納できます。入れ子構造にしない1次元配列も実務では使用されることがあります。また逆に使用する機会はすくないですが、3次元配列や4次元配列も使用することができます。
いろいろな次元の配列
サンプルコード(クリックでコピー)
Private Sub MultiDimentionArr()
Dim multi_arr(0 To 5, 0 To 3, 0 To 2)
Stop
End Sub
Private Sub SingleDimentionArr()
Dim single_arr(0 To 5)
Stop
End Sub
実行結果
上記サンプルコードを実行すると、「Stop」で動作が停止します。ローカルウィンドウで中身を見てみましょう。以下はサンプルコード上段の実行結果です。

多次元配列をReDim(Preserve)するときの注意点
動的配列はマクロの実行中に何度でもReDimで再定義することができます。またその際Preserveキーワードを指定すると入力済の値を保持したまま、配列のサイズを変更できます。ただし、多次元配列に対してこの操作をする場合、要素数の変更は最大の次元数にのみ有効です。例えば、2次元配列の1次元目の要素数はPreserveキーワードを指定していると変更することができません。
サンプルコード(クリックでコピー)
Private Sub PreserveError()
Dim multi_arr()
ReDim multi_arr(0 To 5, 0 To 3)
'■これはエラーにならない
ReDim Preserve multi_arr(0 To 5, 0 To 4)
'■これはエラーになる
ReDim Preserve multi_arr(0 To 6, 0 To 4)
End Sub
実行結果
上記サンプルコードを実行すると、1次元目の要素数を変更するコードを実行する際にエラーが発生します。
配列の要素数を取得する
実務では配列に格納する要素数がマクロの実行前に確定していないことが多いです。ユーザーの入力数やデータの読み取り件数に応じて動的配列を定義し、それを1レコードずつ処理するといった場面が想定されます。そんな時には、Lbound関数とUbound関数を使用します。
Lbound関数とUbound巻子
Lbound関数は指定した配列の最小の要素を取得します。
Ubound関数は指定した配列の最大の要素を取得します。
サンプルコード(クリックでコピー)
Private Sub LboundUbound()
Dim multi_arr()
Dim x As Long, y As Long, i As Long, j As Long
x = Application.InputBox("0以上の整数を入力する", "配列のベースインデックスを入力する", 0, 500, 300, , , 1)
If Not IsNumeric(x) Then Exit Sub
y = Application.InputBox("配列の要素数を0以上の値で入力する", "配列の要素数", 5, 500, 300, , , 1)
If Not IsNumeric(y) Then Exit Sub
ReDim multi_arr(x To x + y, x To x + y * 2)
Call Msgbox(LBound(multi_arr, 1) & " To " & UBound(multi_arr, 1) & vbCrLf & LBound(multi_arr, 2) & " To " & UBound(multi_arr, 2))
For i = LBound(multi_arr, 1) To UBound(multi_arr, 1) Step 1
For j = LBound(multi_arr, 2) To UBound(multi_arr, 2) Step 1
multi_arr(i, j) = i * j
Next j
Next i
Stop
End Sub
実行結果
上記サンプルコードを実行すると、入力値に応じて動的配列の再定義をします。配列の要素数をメッセージに表示し、その後、配列のすべての要素に1次元目と2次元目のインデックスを乗算した結果を格納します。「Stop」でマクロが停止するので、格納された内容はローカルウィンドウから確認してください。

配列の要素に関する注意点
配列の要素数を指定するとき、ここまで「1 To 3」や「0 To 5」などのように配列の最小の要素と最大の要素を指定してきました。しかし要素数の指定は「3」や「5」などのように最小の要素を省略することができます。省略した場合、最小要素は「0」が指定されたものとみなされます。
サンプルコード(クリックでコピー)
Private Sub ReDimOmission()
Dim buf_arr(3)
Call Msgbox(LBound(buf_arr, 1) & " To " & UBound(buf_arr, 1))
End Sub
実行結果
上述のサンプルコードを実行すると、「buf_arr」の要素数をメッセージで表示します。最小の要素を省略した場合、「0」が指定されたものとみなすことが確認できます。
Split関数
VBAで用意されている関数の中には、配列を戻り値とするものもあります。Split関数は文字列を指定した文字列で分割して配列に格納する関数です。
サンプルコード(クリックでコピー)
Private Sub SplitTest()
Dim return_arr() As String
return_arr = Split("A0BC0DEF0H", 0)
Stop
End Sub
実行結果
上記サンプルコードを実行すると、「Stop」でマクロが停止します。配列には文字列が「0」で分割された状態で格納されています。

Comment