ByVal(値渡し)を初心者向けに説明
要点の一行説明ByVal は「値だけを渡す」方式。呼び出し側の変数そのものではなく「変数に入っている値のコピー」を渡すので、呼び出された側で引数の値を変えても、呼び出し元の変数は変わりません。
イメージで理解する
- 呼び出し元の変数 = 「本物のコイン(100円)」
ByValで渡されるもの = 「そのコインの写真(コピー)」- 呼び出し側(写真)を燃やしても、持ってる本物のコインは無事。
これが ByRef(参照渡し)だと:
- 渡されるもの = 「本物のコインへのアクセス(コインの場所が書かれた紙)」
- 呼び出された側でコインそのものを取り替えれば、呼び出し元のコインも変わるイメージ。
具体例 1:整数を渡す(ByVal の基本)
Sub TestByVal_Integer()
Dim x As Integer
x = 10
MsgBox "呼び出し元:処理前 x = " & x ' -> 10
Call AddFive(ByVal x)
MsgBox "呼び出し元:処理後 x = " & x ' -> 10(変わらない)
End Sub
Sub AddFive(ByVal num As Integer)
num = num + 5
MsgBox "AddFive 内:num = " & num ' -> 15
End Sub
VB解説:
AddFiveに渡されるのはxのコピー。numを変更しても元のxは変わらない。
具体例 2:文字列を渡す(ByVal)
Sub TestByVal_String()
Dim s As String
s = "こんにちは"
MsgBox "呼び出し元:処理前 s = " & s
Greet(ByVal s)
MsgBox "呼び出し元:処理後 s = " & s ' -> こんにちは(変わらない)
End Sub
Sub Greet(ByVal msg As String)
msg = msg & "、VBAへようこそ!"
MsgBox "Greet 内:msg = " & msg
End Sub
VB解説:
- 文字列も同じようにコピーが渡るので、呼び出し元は保護される。
重要な補足:オブジェクトや配列は少し注意
- オブジェクト(たとえば
WorkbookやRange)をByValで渡すと、オブジェクト参照のコピーが渡ります。
→ 呼び出し先でオブジェクトの「プロパティや中身」を変更すると、呼び出し元に影響が出ます(同じ実体を指すため)。
→ ただし、呼び出し先で引数に別の新しいオブジェクトをSetしても、それは呼び出し元の変数を置き換えません(参照のコピーを置き換えるだけ)。 - 配列は状況によるが、通常は参照が渡るため中身を変更すると呼び出し元に反映されます(配列の扱いは注意)。
短く言うと:
ByVal= 値(プリミティブ型:数値・文字列)は完全コピー → 呼び出し元に影響なしByVal+ オブジェクト = 参照のコピー → オブジェクト内部の変更は反映される
ByRef と比較(補足で覚えておくと便利)
ByRef は参照渡し。呼び出し元の変数そのものにアクセスできるので、呼び出された側で値を変えると呼び出し元も変わります。
例:
Sub TestByRef()
Dim a As Integer
a = 10
ChangeValue a ' ByRef がデフォルト(引数指定が無ければByRef)
MsgBox a ' -> 20(変更される)
End Sub
Sub ChangeValue(ByRef v As Integer)
v = 20
End Sub
VBよくあるミスと注意点(初心者向け)
- 型の不一致
引数の型(Integer, String など)が呼び出し側と合っていないとエラーか想定外の動作になる。
→ 型を宣言して渡す習慣をつけると良い(例:ByVal num As Integer)。 - オブジェクトの振る舞いの勘違い
オブジェクトは参照で動くので、ByValでも中身を書き換えると呼び出し元に反映される。混乱しやすいポイント。 - Optional(任意引数)や既定のByRef
- 引数を省略できる
Optionalや、引数指定が無いとデフォルトでByRefになることは知っておくと便利だが、まずはByVal/ByRefを明示して使うことをおすすめします。
- 引数を省略できる
練習問題(初心者向け)
各コードは 標準モジュール に貼って実行してみてください。
問題1:値が変わるか確かめよう(整数)
次を実行すると Main1 の最後に表示される x の値はいくつになりますか?(実際に動かして答え合わせしてみてください)
Sub Main1()
Dim x As Integer
x = 5
Call Increase(ByVal x)
MsgBox "Main1 の x = " & x
End Sub
Sub Increase(ByVal n As Integer)
n = n + 10
End Sub
VB答えと解説Main1 の x = 5 。ByVal なので x のコピーが渡り、Increase 内での変更は呼び出し元に反映されない。
問題2:文字列を変更してみよう
次の実行結果は?
Sub Main2()
Dim s As String
s = "A"
AppendText s
MsgBox "Main2 の s = " & s
End Sub
Sub AppendText(ByVal t As String)
t = t & "B"
End Sub
VB答えMain2 の s = A 。ByVal のため元の s は変わらない。
問題3(少し難しい):Range(オブジェクト)を変更する
次を実行したとき、シートのセル A1 に表示される値は何になるか?
Sub Main3()
Dim rng As Range
Set rng = ThisWorkbook.Worksheets(1).Range("A1")
rng.Value = "元"
ChangeRangeByVal rng
MsgBox "A1 = " & ThisWorkbook.Worksheets(1).Range("A1").Value
End Sub
Sub ChangeRangeByVal(ByVal r As Range)
r.Value = "変更された"
End Sub
VB答えと解説
セル A1 は "変更された" になります。理由:ByVal で渡してもオブジェクト参照のコピーが渡されるだけなので、r.Value = ... によるオブジェクト内部の変更は呼び出し元に反映される。
追加の実践例:割り切りチェック(元ページの例を拡張)
対象の数字を受け取り、2 で割り切れるか判定してメッセージを出す例:
Sub TestDivisible()
Dim n As Integer
n = 12
Call WarikireCheck(ByVal n)
End Sub
Sub WarikireCheck(ByVal num As Integer)
If num Mod 2 = 0 Then
MsgBox num & " は 2 で割り切れます。"
Else
MsgBox num & " は 2 で割り切れません。"
End If
End Sub
VBこのように汎用プロシージャを作れば、引数を変えるだけで何度でも使えます。
まとめ(初心者が押さえるべきポイント)
ByValは「値のコピー」を渡す:呼び出し元の変数は守られる。- オブジェクトは例外的に注意:
ByValでもオブジェクトの中身を変更すると呼び出し元に影響する。 ByRefと比較して違いを理解しておくとデバッグが楽になる。- 実際にコードをコピーして動かすと理解が早い。まずは上の例と練習問題を実行してみてください。
