「VBAを使っていると、コードが複雑になってよく分からなくなる...」
そんな悩みありませんか?

・複数の処理をまとめて呼び出す方法がわからない…
・関数に渡したデータをそのまま使いたい、でもどうすればいい?
・関数にデータを渡した後、そのデータを呼び出し元にも反映させたい!



その場合は、VBAの
「Callステートメント 、 ByVal(値渡し)・ByRef(参照渡し)」を
使用すると解決します!
この記事では、Excel VBAの「Callステートメント 、 ByVal(値渡し)・ByRef(参照渡し)」の使い方について詳しく説明します。
VBAでプログラムを書く際、最初は簡単な1つの処理を組み立てることから始めます。
しかし、プログラムが大きくなると、処理を分けたり、複数のプロシージャを使って再利用できるようにしたりすることが重要です。
「Callステートメント」を使うことで、複数のプロシージャ(処理のかたまり)をうまく呼び出し、VBAコードをすっきり整理できるようになります。
また、「ByVal」と「ByRef」の使い方についても、詳しく解説していきます。
ぜひ最後までお読みください。
【 この記事の概要 】
よく使う度 | |
難しさ | |
覚えておくと安心度 |
この記事を読むとできること
- 複数の処理を効率よく呼び出す「Callステートメント」を習得する
- 「ByVal (値渡し)」と「ByRef (参照渡し)」の違いを理解する
- 「ByVal (値渡し)」と「ByRef (参照渡し)」の使用例を確認する
- 「Subプロシージャ」と「Functionプロシージャ」の使い分けを理解する
複数の処理をまとめる「Callステートメント」とは?
「Callステートメント」は、簡単に言うと、他のプロシージャ(処理のまとまり)を呼び出して実行するためのコマンドです。
例えば、同じ処理を何度も書く代わりに、1つのプロシージャにまとめておいて、必要な時にそのプロシージャを「Call」することで処理を行うことができます。
また、プロシージャを分けて書くことで、コードの変更やデバッグを簡単に行うこともできます。
Callを使わないと管理が大変。。。


Callを使うと管理が簡単


Callステートメントの基本的な使い方
例として、2つのプロシージャを使って、1つ目のプロシージャで「こんにちは」と表示し、2つ目のプロシージャで「さようなら」と表示する例を考えてみましょう。
Sub MainProcedure()
Call Greet '// Greetプロシージャを呼び出し
Call Farewell '// Farewellプロシージャを呼び出し
End Sub
Sub Greet()
MsgBox "こんにちは"
End Sub
Sub Farewell()
MsgBox "さようなら"
End Sub
処理結果


どのように動作したのか?


このように、「Callステートメント」を使うことで、他のプロシージャを実行することができます。
これにより、処理を分けて整理し、コードを簡潔に保つことができます。
ByVal (値渡し)とByRef (参照渡し)の違いって?
VBAでは、プロシージャにデータを渡す際に「ByVal (値渡し)」または「ByRef (参照渡し)」という方法を使います。
この2つの方法は、渡したデータの扱い方に違いがあります。
ByVal (値渡し)とは?
ByValは「値渡し」です。
データをコピーしてプロシージャに渡します。
元のデータはそのままで、呼び出し元には影響を与えません。
Sub MainProcedure()
Dim num As Long
num = 5
Call ChangeValueByVal(num)
MsgBox "呼び出し元の値: " & num
End Sub
Sub ChangeValueByVal(ByVal num As Long)
num = num + 10
MsgBox "内部の値(ByVal): " & num
End Sub
処理結果


解説


このように、「ByVal (値渡し)」を使用すると、呼び出し先(ChangeValueByVal)で、変数「num」を変更しても、呼び出し元(MainProcedure)に戻ってきた際に、変数「num」の値は変化しません。
ByRef (参照渡し)とは?
ByRefは「参照渡し」です。
データそのものを渡します。
プロシージャ内でデータが変更されると、呼び出し元のデータも変更されます。
Sub MainProcedure()
Dim num As Long
num = 5
Call ChangeValueByRef(num)
MsgBox "呼び出し元の値: " & num
End Sub
Sub ChangeValueByRef(ByRef num As Long)
num = num + 10
MsgBox "内部の値(ByRef): " & num
End Sub
処理結果


解説


このように、「ByRef (参照渡し)」を使用すると、呼び出し先(ChangeValueByRef)で、変数「num」を変更すると、呼び出し元(MainProcedure)に戻ってきた際に、変数「num」の値が変化します。
ByVal (値渡し)とByRef (参照渡し)を省略した場合
「ByVal (値渡し)」や「ByRef (参照渡し)」について解説しました。
この「ByVal (値渡し)」や「ByRef (参照渡し)」は、省略しても問題なく動作します。
その場合は「デフォルトの「ByRef (参照渡し)」になる」ということを覚えておきましょう。


しかし、常に「ByRef (参照渡し)」にするべきではありません。
理由として、予期しない変更が発生する可能性があるためです。
たとえば、別のプロシージャで変数の値を意図せず変更してしまうと、後続の処理に悪影響を与えることがあるためです。
意図的に値を変更したくない場合は、「ByVal (値渡し)」を明示的に指定するようにしましょう。
ByVal (値渡し)・ByRef (参照渡し)を使用する際の注意ポイント
Callステートメントを使う際のByVal (値渡し) とByRef (参照渡し) の注意ポイントを解説します。
注意1: 「ByVal (値渡し)」と「ByRef (参照渡し)」の使い分け
「呼び出し元」の値を変更せず保持したい場合は、「ByVal (値渡し)」を使いましょう。
逆に、変更した値を「呼び出し元」にも反映させたい場合は「ByRef (参照渡し)」を使います。
しかし、すべてを「ByRef (参照渡し)」にすると予期しない変更が発生することがあるので注意が必要です。
「ByRef (参照渡し)」は慎重に使用しましょう。
注意2: 省略した場合は「ByRef (参照渡し)」
明示的に指定しない場合は、デフォルトで「ByRef (参照渡し)」になります。
意図しない変更を避けるため、変更を加えたくない場合は「ByVal (値渡し)」を指定する習慣をつけましょう。
ByVal (値渡し)・ByRef (参照渡し)の使用例をご紹介
ここからは、「動的配列」の使用例を紹介します。
どれも実際に動作するコードなので、ぜひ実行してみてください。
VBAの基礎から応用までを効率よくしっかり学習したい方には、UdemyのVBA講座もおすすめです。
特に自己学習で進める場合、ビデオや実践例があると理解が進みやすくなります。


\ 自分のペースで学べるVBA講座はこちら /
使用例1: 表の削除と後続処理 … ByVal (値渡し)
指定された範囲の表を削除し、その後の処理を実行する方法を紹介します。
この例では、CurrentRegionを使って範囲を指定し、ByVal (値渡し)を用いて削除処理を行います。
主な使用用途
表全体を一時的に操作し、その後の処理に影響を与えないようにする場合に便利です。
Sub MainProcedure()
Dim targetRange As Range
Set targetRange = Sheet1.Range("A1").CurrentRegion
'// ByValを使って表を削除
Call DeleteTable(ByVal targetRange)
'// 他の処理を続ける
MsgBox "表が削除されました。"
End Sub
Sub DeleteTable(ByVal rng As Range)
rng.ClearContents
MsgBox "指定された範囲の内容が削除されました。"
End Sub
処理結果


コードの動作概要
CurrentRegionを使用して、Sheet1の「A1」セルを含む表全体を対象範囲として設定します。
ByValを使って、指定した範囲targetRangeをDeleteTableサブルーチンに渡します。
ここで指定された範囲内の内容が削除されます。
表の内容が削除された後に、メッセージを表示し、他の処理を続行します。
このコードで使用している機能
1行ずつコードを解説
「クリック」してコードの詳細解説を見る
- Sub MainProcedure()
- この行では、新しいサブルーチン「MainProcedure」を定義しています。
このサブルーチンは、指定した範囲の表を削除し、その後の処理を行うための一連の操作を実行します。
- この行では、新しいサブルーチン「MainProcedure」を定義しています。
- Dim targetRange As Range
- ここでは、「targetRange」という名前の変数をRange型で宣言しています。
この変数は、シート上の特定の範囲を指定するために使用されます。
- ここでは、「targetRange」という名前の変数をRange型で宣言しています。
- Set targetRange = Sheet1.Range(“A1”).CurrentRegion
- この行では、targetRangeに「Sheet1」の「A1」セルを基準とした表全体の範囲を設定しています。
CurrentRegionは、「A1」セルから周囲の空白セルまでの範囲を自動的に選択します。
- この行では、targetRangeに「Sheet1」の「A1」セルを基準とした表全体の範囲を設定しています。
- Call DeleteTable(ByVal targetRange)
- この行では、「DeleteTable」という別のサブルーチンを呼び出しています。
ByVal (値渡し)を使って「targetRange」を引数として渡し、その範囲内の内容を削除します。
ByVal (値渡し)を使うことで、削除対象の範囲をコピーして操作するため、元の範囲には影響がありません。
- この行では、「DeleteTable」という別のサブルーチンを呼び出しています。
- MsgBox “表が削除されました。”
- この行では、メッセージボックスを表示し、「表が削除されました」というメッセージをユーザーに知らせます。
MsgBox関数を使うことで、処理が完了したことを簡単に通知できます。
- この行では、メッセージボックスを表示し、「表が削除されました」というメッセージをユーザーに知らせます。
- End Sub
- この行は、「MainProcedure」サブルーチンの終了を示しています。
ここでこのサブルーチンのすべての処理が完了し、プログラムは次の処理に進みます。
- この行は、「MainProcedure」サブルーチンの終了を示しています。
- Sub DeleteTable(ByVal rng As Range)
- この行では、新しいサブルーチン「DeleteTable」を定義しています。
このサブルーチンは、指定された範囲(rng)の内容を削除するために使われます。
ここで指定されている「rng」は、「targetRange」の範囲のことです。
- この行では、新しいサブルーチン「DeleteTable」を定義しています。
- rng.ClearContents
- この行では、「rng」で指定された範囲内のすべてのセルの内容を削除します。
セルの中身だけがクリアされ、セルのフォーマット(色や罫線など)はそのまま残ります。
- この行では、「rng」で指定された範囲内のすべてのセルの内容を削除します。
- MsgBox “指定された範囲の内容が削除されました。”
- この行では、メッセージボックスを表示し、「指定された範囲の内容が削除されました」というメッセージをユーザーに知らせます。
- End Sub
- この行は、「DeleteTable」サブルーチンの終了を示しています。
すべての処理が完了し、サブルーチンは終了します。
- この行は、「DeleteTable」サブルーチンの終了を示しています。
ByValを使うことで、削除操作が呼び出し元に影響を与えないように処理されます。
削除後にメッセージボックスを表示して、処理が正常に完了したことをユーザーに知らせることで、簡単で効果的なフィードバックが得られます。
また、DeleteTableサブルーチンでは、指定された範囲内のセルの内容をクリアし、もう一度メッセージボックスで操作の結果を通知しています。
このサブルーチンは、簡潔で効果的なコードの構造を示しており、特定の処理を別のサブルーチンに分けることで、コードの可読性と再利用性が向上します。
使用例2: 配列を条件分岐する … ByRef (参照渡し)
配列を別のサブルーチンに渡し、その内容を条件分岐で処理して結果を返す方法を紹介します。
この例では、ByRef (参照渡し) を使用して配列を直接操作し、その結果を呼び出し元に反映させます。
主な使用用途
ByRef (参照渡し) を使用して、配列の内容をサブルーチン内で変更し、その結果を呼び出し元に反映させたい場合に便利です。
たとえば、配列内のデータを条件に基づいて加工し、その変更を後続の処理に利用したい場合に、この方法が有効です。
Sub MainProcedure_使用例2()
Dim numArray() As Variant
numArray = Array(1, 2, 3, 4, 5)
'// ByRefを使って配列を処理
Call ProcessArray(numArray)
'// 処理後の結果を表示
MsgBox "配列の処理が完了しました: " & Join(numArray, ", ")
End Sub
Sub ProcessArray(ByRef arr() As Variant)
Dim i As Long
For i = LBound(arr) To UBound(arr)
If arr(i) Mod 2 = 0 Then
arr(i) = arr(i) * 2 '// 偶数なら2倍にする
Else
arr(i) = arr(i) + 5 '// 奇数なら5を加える
End If
Next i
End Sub
処理結果


コードの動作概要
Array関数を使用して、numArrayという動的配列を初期化します。
この段階で配列に「1, 2, 3, 4, 5」という値が格納されます。
ByRefを使ってnumArray配列をProcessArrayサブルーチンに渡します。
サブルーチン内でForループを使用し、配列の各要素に対して処理を行います。
具体的には、偶数であればその要素を「2倍」にし、奇数であれば「5」を加えます。
処理が完了した配列の内容を、Join関数を使ってカンマ区切りの文字列に変換し、メッセージボックスで表示します。
これにより、配列がどのように変更されたかを確認できます。
このコードで使用している機能
1行ずつコードを解説
「クリック」してコードの詳細解説を見る
- Sub MainProcedure()
- この行では、新しいサブルーチン「MainProcedure」を定義しています。
このサブルーチンは、配列を初期化し、その配列を別のサブルーチンで処理するための手順を実行します。
- この行では、新しいサブルーチン「MainProcedure」を定義しています。
- Dim numArray() As Variant
- ここでは、「numArray」という名前の配列をVariant型として宣言しています。
この配列は、複数の数値を格納するために使用されます。
Array関数を使用する場合は、型を「Variant」にする必要があります。
- ここでは、「numArray」という名前の配列をVariant型として宣言しています。
- numArray = Array(1, 2, 3, 4, 5)
- この行では、配列「numArray」に「1, 2, 3, 4, 5」という5つの数値を格納しています。
Array関数を使って、一度に複数の数値を配列に設定します。
- この行では、配列「numArray」に「1, 2, 3, 4, 5」という5つの数値を格納しています。
- Call ProcessArray(ByRef numArray)
- この行では、「ProcessArray」という別のサブルーチンを呼び出しています。
ByRef (参照渡し)を使って、配列「numArray」そのものを引数として渡し、その内容を処理します。
ByRef (参照渡し)を使うことで、配列の変更が呼び出し元にも反映されます。
- この行では、「ProcessArray」という別のサブルーチンを呼び出しています。
- MsgBox “配列の処理が完了しました: ” & Join(numArray, “, “)
- この行では、MsgBox関数を使って、処理後の配列の内容をメッセージボックスで表示します。
Join関数を使って、配列の要素をカンマで区切った文字列として表示します。
- この行では、MsgBox関数を使って、処理後の配列の内容をメッセージボックスで表示します。
- End Sub
- ここの行は、「MainProcedure」サブルーチンの終了を示しています。
ここでこのサブルーチンのすべての処理が完了し、プログラムは次の処理に進みます。
- ここの行は、「MainProcedure」サブルーチンの終了を示しています。
- Sub ProcessArray(ByRef arr() As Variant)
- この行では、新しいサブルーチン「ProcessArray」を定義しています。
このサブルーチンは、渡された配列を処理するために使用されます。
ByRef (参照渡し)を使って配列「arr」そのものを受け取り、その内容を直接変更します。
- この行では、新しいサブルーチン「ProcessArray」を定義しています。
- Dim i As Long
- ここでは、「i」という名前の変数をLong型で宣言しています。
この変数は、配列内の各要素にアクセスするためのインデックスとして使用されます。
- ここでは、「i」という名前の変数をLong型で宣言しています。
- For i = LBound(arr) To UBound(arr)
- この行では、Forループを開始しています。
LBound関数で配列の最初のインデックス(通常は0)を取得し、UBound関数で配列の最後のインデックスを取得します。
この範囲内でループを繰り返し、配列の各要素を順番に処理します。
- この行では、Forループを開始しています。
- If arr(i) Mod 2 = 0 Then
- この行では、配列の現在の要素が偶数かどうかを確認しています。
Mod 2を使って「2」で割った余りが「0」なら、その数は偶数です。
- この行では、配列の現在の要素が偶数かどうかを確認しています。
- arr(i) = arr(i) * 2
- この行では、配列の現在の要素が偶数であれば、その値を2倍にします。
- Else
arr(i) = arr(i) + 5- この行では、配列の現在の要素が奇数であれば、その値に「5」を加えます。
Elseは、Ifの条件が成立しなかった場合に実行される部分です。
- この行では、配列の現在の要素が奇数であれば、その値に「5」を加えます。
- End If
- この行は、If条件文の終了を示しています。
ここで条件分岐の処理が完了します。
- この行は、If条件文の終了を示しています。
- Next i
- この行では、次のインデックスに移動し、Forループを繰り返します。
すべての配列の要素が処理されるまで、ループが続行されます。
- この行では、次のインデックスに移動し、Forループを繰り返します。
- End Sub
- この行は、ProcessArrayサブルーチンの終了を示しています。
すべての処理が完了し、サブルーチンは終了します。
- この行は、ProcessArrayサブルーチンの終了を示しています。
ByRefを使用することで、配列そのものがサブルーチンに渡され、処理結果が呼び出し元にも反映される仕組みを理解できました。
これにより、配列の内容をサブルーチン内で自由に操作し、その変更を次の処理に活かすことが可能になります。
SubプロシージャとFunctionプロシージャの使い分け
Callステートメントを使う際、もう1つ重要なのが「Sub」と「Function」の使い分けです。
プロシージャには、「Subプロシージャ」と「Functionプロシージャ」の2種類があり、それぞれ用途が異なります。
これらは使い方が異なるため、使い分けが重要です。
「Sub」は処理を行うだけで結果を返しませんが、「Function」は結果を返します。
そのため、計算結果や処理結果を返す必要がある場合は、「Function」を使う方が適しています。
Subプロシージャを使うべき場合
- 処理を行うだけで、結果を返す必要がない場合
- 例えば「セルの値を変更」「メッセージを表示」したりする場合に使います。
Functionプロシージャを使うべき場合
- 計算や条件分岐によって結果を返す必要がある場合。
- 複数のプロシージャで同じ計算ロジックを使いたい場合。
「Functionプロシージャ」を使用すると、結果を返すことができます。
結果が必要な場合は「Function プロシージャ」、処理を行うだけであれば「Sub プロシージャ」を使うようにしましょう。
「Functionプロシージャ」については、別記事で解説を行っています。
合わせてご覧ください。


この記事のまとめ
Callステートメントを使うことで、VBAのコードを整理してシンプルに書くことができました。
また、ByVal (値渡し)とByRef (参照渡し)を使い分けることで、データの渡し方に柔軟性を持たせることができます。
ポイントのおさらい
- Callステートメントでコードを整理
- Callステートメントは、別のプロシージャ(SubやFunction)を呼び出して実行するコマンドです。
処理を分けて整理し、同じ処理を何度も書く必要がなくなります。
プロシージャを再利用することで、コードが簡潔になり、保守性も向上します。
⇒ 「複数の処理をまとめる「Callステートメント」とは?」もう一度見る。
- Callステートメントは、別のプロシージャ(SubやFunction)を呼び出して実行するコマンドです。
- ByVal (値渡し)とByRef (参照渡し)の使い分け
- ByVal (値渡し)
- ByVal (値渡し)は、変数の「コピー」をプロシージャに渡します。
プロシージャ内で変数を変更しても、呼び出し元の変数には影響がありません。
例えば、プロシージャ内で変数を一時的に操作したいが、呼び出し元の値はそのままにしたい場合に使います。
⇒ 「ByVal (値渡し)とは?」もう一度見る。
- ByVal (値渡し)は、変数の「コピー」をプロシージャに渡します。
- ByRef (参照渡し)
- ByRef (参照渡し)は、変数そのものをプロシージャに渡します。
プロシージャ内で変数が変更されると、呼び出し元の変数もその変更が反映されます。
例えば、プロシージャで変数を更新し、その結果を呼び出し元に反映させたい場合に使います。
⇒ 「ByRef (参照渡し)とは?」もう一度見る。
⇒ 「ByVal (値渡し)とByRef (参照渡し)を省略した場合」もう一度見る。
- ByRef (参照渡し)は、変数そのものをプロシージャに渡します。
- ByVal (値渡し)
- ByVal (値渡し)とByRef (参照渡し)の使用例
- ByValを使った表の削除と後続処理
- ByValを使用して表を削除する例を紹介しました。
この方法では、表の範囲をプロシージャに渡し、内容を削除した後に別の処理を続けることができます。
⇒ 「使用例1: 表の削除と後続処理」もう一度見る。
- ByValを使用して表を削除する例を紹介しました。
- ByRefを使った配列の処理と返却
- ByRefを使用して配列を処理する例を紹介しました。
この方法では、配列をプロシージャに渡し、配列内の各要素に対して処理を行い、その結果を呼び出し元に反映させます。
⇒ 「使用例2: 配列を条件分岐する」もう一度見る。
- ByRefを使用して配列を処理する例を紹介しました。
- ByValを使った表の削除と後続処理
- SubプロシージャとFunctionプロシージャの使い分け
- Subプロシージャ
- Subプロシージャは、特定の処理を行うためのコードブロックで、結果を返さないのが特徴です。
たとえば、セルの値を変更したり、メッセージを表示したりする場合に使用します。
結果を返す必要がない処理、つまり何らかのアクションだけを実行する場合には、Subプロシージャを使うべきです。
- Subプロシージャは、特定の処理を行うためのコードブロックで、結果を返さないのが特徴です。
- Functionプロシージャ
- Functionプロシージャは、特定の処理を行い、結果を返すコードブロックです。
計算結果や条件分岐の結果などを他のプロシージャで利用するために使用されます。
計算を行って結果を他のプロシージャに渡したい場合、または何らかの返り値が必要な場合には、Functionプロシージャを使います。
⇒ 「SubプロシージャとFunctionプロシージャの使い分け」もう一度見る。
- Functionプロシージャは、特定の処理を行い、結果を返すコードブロックです。
- Subプロシージャ
このように、CallステートメントやByVal (値渡し)とByRef (参照渡し)の使い分けを理解することで、複数の処理を効率よく整理し、プログラム全体を柔軟かつ効率的に作成できます。

