- 引数とは、Sub・Functionへ渡す値やセル範囲のこと。
ByVal / ByRef は、その引数をどう渡すかを決める書き方です。 - 元の値を変えたくない引数は ByVal(値渡し)、呼び出し元の変数も更新したい引数は ByRef(参照渡し)。
- VBAは省略するとByRef(既定)。初心者はまず ByVal を明示すると事故が減ります。
- ByVal / ByRef は呼び出し側ではなく、Sub・Functionの「受け取る側」に書きます。
引数には他にも Optional(省略可)/ 既定値 / 名前付き引数 / ParamArray(個数自由) があります。
VBAでマクロを複数のプロシージャに分け始めると、こんな場面でつまずきます。
- 別の処理に値を渡しただけなのに、呼び出し元の変数まで勝手に変わってしまった
- 逆に、変えたいのに変わらない
- 「ByValとByRefって、結局どっちを使えばいいの?」
- 「
引数の型が一致しませんというエラーが消えない」
原因のほとんどは、値渡し(ByVal)と参照渡し(ByRef)の違いと、引数の渡し方のルールを押さえていないことにあります。
この記事は、その入口から応用までを1ページにまとめた完全ガイドです。最小コードで違いを体感し、1行ずつのコード解説・メモリのイメージ・省略時の挙動・Callステートメント・Optional・既定値・名前付き引数・ParamArray・配列やオブジェクトの渡し方・よくあるエラーまで、コピペで試せるコード付きで初心者向けに解説します。困ったときに何度でも戻ってこられる「引数の早見表ページ」として使ってください。
| よく使う度 | |
| 難しさ | |
| 実務重要度 |
VBAのByValとByRefの違いを最小コードで理解する

まず「引数」とは、SubやFunctionへ外から渡す値・文字列・セル範囲などのことです。ByVal と ByRef は、その引数を「コピーとして渡すか」「呼び出し元の変数につながったまま渡すか」を決める書き方です。
要点はシンプルで、ByValは「値のコピー」を渡すので呼び出し元は変わらない。ByRefは「変数そのもの」を渡すので呼び出し元も変わる、という違いです。ここからコードとイメージで確認します。
イメージは「箱」で考えると分かりやすいです。
- ByVal(値渡し)
- 元の箱の中身をコピーして、別の箱で作業するイメージ。作業先で書き換えても、元の箱は変わりません。
- ByRef(参照渡し)
- 元の箱そのものを渡すイメージ。作業先で書き換えると、元の箱の中身も変わります。
| 項目 | ByVal(値渡し) | ByRef(参照渡し) |
|---|---|---|
| 渡すもの | 値のコピー | 変数そのもの(の参照) |
| 呼び出し元の変数 | 変わらない | 変わる |
| 向いている場面 | 値を読む・一時的に計算する | 処理結果を呼び出し元へ返す |
| 省略したときの扱い | — | VBAの既定はこちら |
| 速度・メモリ | 大きいデータはコピーで重くなることがある | コピーしないので大きいデータに向く |
| 初心者の判断 | 迷ったらまずこちら | 変更する理由があるときだけ |
まずは Long(整数)の最小コードで、同じ変数を渡したときの違いを見てください。AddByVal と AddByRef という2つの小さな処理に、同じ amount を渡します。
Sub CheckByValAndByRef()
Dim amount As Long
amount = 100
AddByVal amount
MsgBox "ByValの後: " & amount ' 100のまま
amount = 100
AddByRef amount
MsgBox "ByRefの後: " & amount ' 110に変わる
End Sub
'// ByValの場合
Sub AddByVal(ByVal price As Long) ' ByValは「受け取る側」に書く
price = price + 10
End Sub
'// ByRefの場合
Sub AddByRef(ByRef price As Long) ' ByRefも「受け取る側」に書く
price = price + 10
End Sub
最初のメッセージは「ByValの後: 100」、次は「ByRefの後: 110」。
同じ amount を渡しても、ByValは元の値を守り、ByRefは元の値ごと書き換えます。
1行ずつの解説(つまずきやすい行)
| コード | 何をしている? |
|---|---|
Dim amount As Long | 整数を入れる変数 amount を用意する |
AddByVal amount | amount を AddByVal に渡す(呼び出し側にByVal/ByRefは書かない) |
Sub AddByVal(ByVal price As Long) | 受け取る側。ByVal なので price は amount のコピー |
price = price + 10(ByVal側) | コピーを書き換えるだけ。amount には影響しない |
Sub AddByRef(ByRef price As Long) | 受け取る側。ByRef なので price は amount そのもの |
price = price + 10(ByRef側) | amount 自体が書き換わる(110になる) |
AddByVal amount のように、呼び出す側にはByVal/ByRefを書きません。
値渡し・参照渡しの指定は、受け取る側(Subの引数定義)に書きます。
VBAで変数を作ると、PCの作業スペース(メモリ)に値の「置き場所」ができます。
- ByVal は、その値をコピーして新しい置き場所を渡します。だから受け取った側で書き換えても、元の置き場所は無傷です。
- ByRef は、置き場所の住所(参照)そのものを渡します。受け取った側が書き換えると、同じ住所=元の置き場所の中身が変わります。
ByVal(値渡し)とは?元の値が守られる仕組み
ByValは、変数の「中身をコピー」して渡す方法です。プロシージャの中でいくらコピーを書き換えても、呼び出し元の変数は元のまま残ります。Microsoft Learnの引数を効率的に渡す方法でも、「値渡しは元の変数をコピーし、プロシージャ内の変更は元の変数に反映されない」と説明されています。
- 書式
Sub 名前(ByVal 変数名 As 型)
- 向いている場面
- 渡された値を読むだけ・計算に一時的に使うだけのとき
- メリット
- 数値や文字列などの単純な値なら、元の変数を壊しにくい安心感がある。意図しない副作用を防ぎやすい


ByRef(参照渡し)とは?呼び出し元まで書き換わる仕組み
ByRefは、変数「そのもの(への参照)」を渡す方法です。プロシージャ内で書き換えると、呼び出し元の変数まで変わります。VBAでは、指定しない限りすべての引数がByRef(参照渡し)で渡されます。
- 書式
Sub 名前(ByRef 変数名 As 型)
- 向いている場面
- 処理した結果を呼び出し元へ返したいとき/大きなデータを渡すとき(コピーしないので速い)
- 注意
- 意図せず元の変数を変えてしまう事故が起きやすい。変更する理由があるときだけ使う


ByVal・ByRefを省略するとどうなる?(既定はByRef)
VBAでは、ByValもByRefも書かないと、自動的にByRef(参照渡し)になります。これはMicrosoft LearnのSub statementに「ByRef is the default in VBA(VBAではByRefが既定)」と明記されています。
' 下の2つは同じ意味(省略するとByRef)
Sub Add1(price As Long) ' 省略 → ByRef扱い
Sub Add2(ByRef price As Long) ' 明示短いサンプルや古いコードでは省略をよく見かけますが、初心者のうちは意図が伝わりにくく、「気づかないうちに元の変数が変わっていた」という事故につながります。迷ったらByValを明示しておくと安全です。チームでコードを共有する場合も、明示しておくと読み手が迷いません。

VBAでSub・Functionに処理を分ける前に、作業の流れをマインドマップで整理しておくと迷いにくいです
転記・集計・表の整形などをいきなり1つのプロシージャに詰め込もうとすると、「どこをSubやFunctionに分けるか」「何を引数で渡すか」があいまいになりがちです。まず作業手順をマインドマップで分解してから書き始めると、プロシージャの切り分けやByVal/ByRefの使い分けも整理しやすくなります。
※TsunaGraphはPCブラウザ向けのローカルアプリです。無料Lite版やStandard版の違いはリンク先で確認できます。
ByValとByRefの使い分け・注意点・よくあるエラー
要点: 「値を読むだけ」なら ByVal、「結果を呼び出し元へ返したい」なら ByRef。省略は避け、指定は受け取る側に書く。ここでは判断基準と、初心者がハマる落とし穴・エラーをまとめて回収します。
どちらを使う?判断の基準
次の順番で考えると迷いません。
- 渡した値を「読むだけ」「中で一時的に計算するだけ」 → ByVal
- 処理した結果を「呼び出し元の変数に反映したい」 → ByRef
- 返したい結果が1つだけなら → ByRefより Functionの戻り値の方が読みやすい(後述)
- 渡すデータがとても大きい(長い文字列・大きな配列など)→ コピーを避けたいので ByRef(配列はByRef必須)
| やりたいこと | おすすめ | 理由 |
|---|---|---|
| 計算用に値を渡すだけ | ByVal | 元を壊さない |
| 1つの結果を返す | Function戻り値 | 意図が明確 |
| 複数の結果をまとめて返す | ByRef | 複数の変数を更新できる |
| 配列を渡す | ByRef | VBAは配列をByValで渡せない(後述) |
初心者がやりがちな間違いと失敗例
間違い1: 呼び出し側にByVal/ByRefを書いてしまう
ByVal/ByRefは「受け取る側(定義側)」に書きます。呼び出し側に書くのは誤りです。
' ❌ 間違い:呼び出し側にByValを書いている
Call DeleteTable(ByVal targetRange)
' ⭕ 正しい:呼び出しはそのまま、ByValは受け取る側に書く
Call DeleteTable(Range("A1"))
Sub DeleteTable(ByVal rng As Range)
rng.CurrentRegion.ClearContents
End Sub間違い2: 余分な丸かっこで、意図せず「値渡し」になる
これは気づきにくい落とし穴です。Callを使わずに呼ぶとき、引数を余分な丸かっこで囲むと、その引数は値渡しとして評価され、ByRefにしても呼び出し元が変わりません(Microsoft Learnのコードでのかっこの使用)。
Sub Main_Paren()
Dim n As Long
n = 100
AddByRef (n) ' ❌ 余分なかっこ → 値渡しになり n は 100 のまま
MsgBox n ' 100(ByRefなのに変わらない!)
AddByRef n ' ⭕ かっこなし → ByRefが効いて n が変わる
MsgBox n ' 110
End Sub「ByRefにしたのに変わらない」ときは、まず余分なかっこが付いていないかを疑ってください。
よくある失敗 → 原因 → 対策
| 失敗(症状) | 原因 | 対策 |
|---|---|---|
| ByRefなのに呼び出し元が変わらない | 余分なかっこ (x) で値渡しになっている | かっこを外す、または Call 名前(x) の形に統一 |
| 値を変えたくないのに変わってしまう | 省略でByRefになっている | ByVal を明示する |
| 呼び出し側のByVal/ByRefでエラー | 指定する場所を間違えている | ByVal/ByRefは受け取る側に書く |
Range・オブジェクトの注意とトラブルシュート
ここは初心者が一番混乱しやすいポイントです。
RangeをByValで渡してもセルは消える
ByVal rng As Range と書いても、rng.ClearContents を実行すればシート上のセルは実際に消えます。ByValは「rng という変数の参照先の差し替えを呼び出し元へ返さない」という意味であって、Rangeが指すセルへの操作を取り消す意味ではありません。RangeやWorksheetのようなオブジェクト変数は、ByValでも「同じ実体」を指し続けるためです。値渡し・参照渡しの違いは、まず Long や文字列などの単純な値で理解し、Rangeなどのオブジェクトはその後で押さえると混乱しにくいです。
症状: 「ByRef 引数の型が一致しません」(コンパイルエラー)
- 原因
- 呼び出し側の変数と、受け取る側の引数の型が違う。ByRefは同じ置き場所を共有するため、型が完全に一致していないと渡せません(例:
Variantを渡してLongで受け取る、Integerを渡してLongで受け取る)。
- 呼び出し側の変数と、受け取る側の引数の型が違う。ByRefは同じ置き場所を共有するため、型が完全に一致していないと渡せません(例:
- 解決
- 両方の型をそろえる。どうしてもそろえられない/コピーで十分なら、受け取る側を
ByValにすると型変換が効いて通ることがあります。
- 両方の型をそろえる。どうしてもそろえられない/コピーで十分なら、受け取る側を
症状:「Array argument must be ByRef(配列引数はByRefにする必要があります)」
- 原因
- 配列を
ByValで受け取ろうとした。VBAでは配列をそのまま値渡しできません(Microsoft LearnのArray argument must be ByRef)。
- 配列を
- 解決
- 配列は
ByRef(または省略)で受け取る。どうしてもコピーを渡したいときは、Variantで受け取る方法があります。
- 配列は
ByValとByRefだけでなく、Sub・Functionの分け方や引数の渡し方まで理解したい方は、VBA講座も確認しておくと迷いにくいです
この記事のコードだけでも、ByVal と ByRef の違いや Call・Optional・ParamArray は試せます。ただ、Sub・Function への処理の分け方や、変数・繰り返し処理・条件分岐をまとめて身につけたい場合は、動画講座で実際の操作画面を見ながら学ぶと理解しやすくなります。
※価格・割引・返金条件・講座内容は変わる場合があります。購入前にUdemy上で最新情報をご確認ください。
Callと引数のいろいろな渡し方【実務・完全版】
要点: Callは別の処理を呼び出す書き方。引数を渡すなら、Callあり=丸かっこで囲む/Callなし=丸かっこなし。さらにVBAには、省略できる引数・既定値・名前付き引数・個数自由の引数といった便利な渡し方があります。
Callステートメントの使い方
処理が長くなると、1つのプロシージャに全部書くのは管理が大変です。処理を分けて「呼び出す」ことで、読みやすく直しやすいコードになります。
条件ごとに呼び出す処理を分けたい場合は、条件分岐そのものを VBAのSelect Caseの使い方 で整理し、各分岐から共通処理を呼び出す形にすると読みやすくなります。
Microsoft LearnのCall statementによると、Callキーワードを使って引数つきで呼ぶ場合は引数を丸かっこで囲む必要があり、Callを省略する場合は丸かっこも省略します(VBAでは「Callと丸かっこは、両方使うか・両方使わないか」)。
Sub MainProcedure()
ShowMessage "こんにちは" ' Callなし → 丸かっこなし
Call ShowMessage("さようなら") ' Callあり → 丸かっこで囲む
End Sub
Sub ShowMessage(ByVal messageText As String)
MsgBox messageText
End Sub引数が複数あるときは、カンマで区切ります。
Sub Main_Call2()
Call ShowUser("山田", 30) ' Callあり → かっこで囲む
ShowUser "鈴木", 25 ' Callなし → かっこなし
End Sub
Sub ShowUser(ByVal userName As String, ByVal age As Long)
MsgBox userName & "さん(" & age & "歳)"
End Sub- 別のモジュールにあるプロシージャも、同じように呼び出せます(同名がなければモジュール名は省略可。重複時は
Module1.ShowUserのように指定)。 Callは付けても付けなくても動きますが、「これは別の処理の呼び出しだ」と読み手に伝わるので、付ける派の人もいます。どちらでも構いませんが、かっこのルールだけは守りましょう。



引数のバリエーション(省略可能・既定値・名前付き・可変個)
ByVal/ByRef以外にも、引数には便利な指定方法があります。実務でよく出てくる順に紹介します。
Optional:省略できる引数にする
Optional を付けると、その引数は渡さなくてもよくなります。Optional引数は引数リストの後ろにまとめて置く必要があります(Microsoft Learnの名前付き引数と省略可能な引数)。
Sub Greet(ByVal userName As String, Optional ByVal title As String = "さん")
MsgBox userName & title
End Sub
Sub Main_Greet()
Greet "山田" ' 山田さん(titleは省略 → 既定値)
Greet "鈴木", "様" ' 鈴木様
End Sub
既定値(デフォルト値)を設定する
上のコードの = "さん" が既定値です。Optional ByVal title As String = "さん" のように、Optional引数には省略されたときに使う値を設定できます。型を指定したうえで既定値を持たせられます。
省略されたかどうかを判定したいときは、Variant 型のOptional引数にして IsMissing を使います(IsMissing はVariant型のときだけ有効です)。
Sub ShowCount(Optional ByVal n As Variant)
If IsMissing(n) Then
MsgBox "件数の指定なし"
Else
MsgBox "件数: " & n
End If
End Sub
名前付き引数で渡す(順序を気にしない)
引数名:=値 の形(コロン+イコール)で渡すと、引数の順序を気にせず指定できます。引数が多いプロシージャや、Optionalを飛ばして指定したいときに便利です。
Sub MakeBox(ByVal width As Long, ByVal height As Long, ByVal color As String)
MsgBox width & "×" & height & " / " & color
End Sub
Sub Main_Named()
MakeBox color:="赤", width:=10, height:=5 ' 順序が違ってもOK
End Sub
ParamArray:個数が決まらない引数を受け取る
渡す個数が決まらないときは ParamArray を使います。ルールは次の通りです(Microsoft Learnのパラメーター配列)。
- 引数リストの最後に1つだけ置ける
- 型は
Variantの配列に限られる ByVal/ByRef/Optionalとは併用できない
Function SumAll(ParamArray nums() As Variant) As Double
Dim v As Variant, total As Double
For Each v In nums
total = total + v
Next v
SumAll = total
End Function
Sub Main_Sum()
MsgBox SumAll(1, 2, 3, 4) ' 10(個数は自由)
MsgBox SumAll(10, 20) ' 30
End Sub
引数キーワード早見表
| キーワード | 何のため | 書き方の例 |
|---|---|---|
ByVal | 値のコピーを渡す(元を守る) | Sub F(ByVal x As Long) |
ByRef | 変数そのものを渡す(VBAの既定) | Sub F(ByRef x As Long) |
Optional | 省略できる引数(後ろに置く) | Optional ByVal x As Long = 0 |
= 既定値 | 省略されたときの値 | Optional ByVal x As Long = 10 |
ParamArray | 個数が決まらない引数(最後・Variant配列) | ParamArray nums() As Variant |
名前付き引数 := | 順序によらず指定 | F color:="赤", width:=10 |
実務での使用例とSub・Functionの使い分け
ここまでの知識を、実際の業務に近い形でまとめます。
SubとFunction、どちらで作る?
- Sub
- 処理を実行するだけ。値を返さない。
- Function
- 処理して値を返せる。式の右側で使える(Microsoft LearnのFunction statement)。
| 作りたいもの | おすすめ |
|---|---|
| 画面表示・セル操作など「実行するだけ」 | Sub |
| 計算結果を1つ返したい | Function(戻り値) |
| 複数の変数をまとめて更新したい | Sub+ByRef |
使用例1:表を削除して後続処理(ByValで元を守る)
目的: 指定セルを含む表全体を削除する。表を消す処理を部品化し、呼び出し元はそのまま使い続けたいので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使いどころ: 「この表だけ消したい」という処理を、他のマクロから何度も呼び出したいとき。
ClearContents と Clear の違いまで確認したい場合は、関連記事の VBA ClearContentsの使い方 も参考になります。表全体を自動でつかむ CurrentRegion の考え方は、VBA CurrentRegionの使い方 で詳しく整理しています。

使用例2:配列を渡して結果を反映(ByRefで返す)
目的: 配列の中身をまとめて加工し、その結果を呼び出し元の配列へ反映する。配列はByRefでしか渡せないので、ByRef(または省略)で受け取ります。
Sub Main_AddTax()
Dim prices(1 To 3) As Long
prices(1) = 100: prices(2) = 200: prices(3) = 300
AddTax prices ' 配列をByRefで渡す
MsgBox prices(1) ' 110(税込みに変わっている)
End Sub
Sub AddTax(ByRef targetPrices() As Long)
Dim i As Long
For i = LBound(targetPrices) To UBound(targetPrices)
targetPrices(i) = targetPrices(i) * 1.1
Next i
End Sub
使いどころ: 複数の値をまとめて加工して、元の配列へ反映したいとき。
注意: Sub AddTax(ByVal targetPrices() As Long) と書くと「Array argument must be ByRef」エラーになります。配列はByRefで渡しましょう。
配列の宣言や LBound / UBound の基本があいまいな場合は、先に VBA 配列の使い方徹底解説 を読んでおくと、この使用例の流れを追いやすくなります。
使用例3:Functionで計算結果を返す
目的: 1つの値を計算して返すだけなら、ByRefよりFunctionの戻り値が分かりやすいです。
Sub Main_UseFunction()
Dim taxIncluded As Long
taxIncluded = AddTaxValue(100) ' 戻り値を受け取る
MsgBox taxIncluded ' 110
End Sub
Function AddTaxValue(ByVal price As Long) As Long
AddTaxValue = price * 1.1 ' 関数名に値を代入して返す
End Function
戻り値のしくみをもっと詳しく知りたい方は、関連記事 VBA Functionプロシージャ入門 もあわせてどうぞ。
- 変数の宣言から復習する: VBAで変数を使いこなす方法
- メッセージ表示の基本を確認する: VBAのMsgBoxの使い方
よくある質問とまとめ
まとめ
- ByValは値のコピー、ByRefは変数そのものを渡す
- 呼び出し元を変えたくないならByVal、結果を返したいならByRef
- VBAは省略するとByRef。初心者は明示が安全
- ByVal/ByRefは呼び出し側ではなく、受け取る側に書く
- 余分なかっこ
(x)は値渡しになるので注意 - 配列はByRefでしか渡せない/1つ返すだけならFunctionの戻り値
- 省略は
Optional+既定値、順序自由は名前付き引数:=、個数自由はParamArray
困ったときに何度でも戻って確認できるよう、冒頭の違いの早見表と引数キーワード早見表をブックマークしておくと便利です。
ByValやByRefで処理を部品化できるようになると、「この手作業、そもそも減らせないか?」という視点が身につきます。その延長で見直したいのが、会議メモや議事録づくり。録音するだけで文字起こしからAI要約までこなすレコーダーを使えば、打ち合わせ後の”書き起こし残業”をまるごと減らせます。


