VBA技術解説
値渡し(ByVal)、参照渡し(ByRef)について

ExcelマクロVBAの問題点と解決策、VBAの技術的解説
公開日:2013年5月以前 最終更新日:2022-06-22

値渡し(ByVal)、参照渡し(ByRef)について


ByValが値渡し、ByRefが参照渡しです。
ここまでは、どこにでも書いてあります。
しかし、なんとなく理解できるけど、なんとなく理解できない・・・


結局のところ実際の活用がなかなか出来ない事が多いようです。
使用例を通じて、理解して下さい。


ByVal(値渡し)とByRef(参照渡し)

ByVal:値渡し

変数の中のデータを渡すもので、呼出側の変数は影響を受けません。
つまり、値渡しでは、呼出し先で引数の値を変更しても、呼出し元の引数は変更されません。

マクロ VBA サンプル画像

変数の値を求めて、その値を渡します。
呼出し先から戻ってきても、変数の値は変化しません。


ByRef:参照渡し

変数そのものを渡すもので、呼出側の変数が影響を受けます。
つまり、参照渡しでは、呼び出し先で引数の値を変更すると、呼び出し元の引数も変更されます。

VBA マクロ 値渡し ByVal 参照渡し ByRef

変数そのものを渡します。
呼出し先で引数の値を変更した場合は、その変更後の引数が呼出し元の変数に入ります。

ただし、呼出元で変数ではなく、関数や数式を引数にした場合や、変数を括弧()で囲んだ場合は値渡し(ByVal)になります。
以下の記述は、呼出し先がByRef(参照渡し)でもByVal(値渡し)になります。
本来は必要のない括弧()を付けている場合
Call 呼び出し先((変数))
呼び出し先 (変数)
変数を評価して値を求めてから、その値を渡すことになります。
VBAでの括弧()の使い方、括弧が必要な場合
・基本文型 ・VBAにおける括弧() ・VBAにおける半角空白の意味 ・戻り値を他の用途に使う時 ・括弧()の使い方の基本文型 ・Callを省略しなければ全て括弧が必要になる ・最後に一言

関数や数式の場合
Call 呼び出し先(関数(変数))
Call 呼び出し先(変数+変数)
関数や数式なので、そのままでは渡すことはできないので、値評価後に値を渡します。


普通の変数(プリミティブ型)の場合

Sub sample1()
  Dim i, j
  i = 1
  j = 1
  Call sub1(i, j)
  MsgBox i
  MsgBox j
End Sub
Sub sub1(arg1, arg2)
  arg1 = arg1 + 1
  arg2 = arg2 + 2
End Sub

上記のsample1を実行すると、2,3の順にメッセージ表示されます。
ByValもByRef指定していないので、省略時のByRefとなっています。
この場合、ByRefなので参照渡しとなり、Call元のプロシージャーの引数も変更されます。

Sub sample2()
  Dim i, j
  i = 1
  j = 1
  Call sub2(i, j)
  MsgBox i
  MsgBox j
End Sub
Sub sub2(ByVal arg1, ByVal arg2)
  arg1 = arg1 + 1
  arg2 = arg2 + 2
End Sub

上記のsample2を実行すると、1,1の順にメッセージ表示されます。
ByValを指定しているので値渡しとなっています。
この場合、Call元のプロシージャーの引数は変更されません。

普通の変数(プリミティブ型)は理解しやすいと思います。


オブジェクト変数の場合

オブジェクト変数については、以下を参照してください。
オブジェクト変数とは何か|VBA技術解説
・そもそも変数とは何か ・変数の入れ物としての大きさは ・オブジェクト変数にオブジェクトは入っていない ・オブジェクト変数のメモリアドレス ・Is演算子によるオブジェクトの比較 ・TypeOf演算子 ・オブジェクト変数を使う時の注意点 ・オブジェクト変数の最後に

引数にオブジェクトを指定した場合に、勘違いしている場合が多いです。

Sub sample()
  Dim MyRange As Range
  Set MyRange = Range("A1")
  Call sample2(MyRange)
End Sub

Sub sample2(ByVal MyRange As Range)
  MyRange.Value = 1
End Sub

上記の結果は、A1セルに1が入ります。
当然ですよね。
つまり、ByValで渡されたオブジェクトに変更を加えれば、呼出し元でもオブジェクトは変更されています。

Range以外の他のオブジェクトでも同じことです。
では、オブジェクトの場合、ByValとByRefの違いは何でしょうか?

オブジェクト変数がByValの場合

Sub sample()
  Dim MyRange As Range
  Set MyRange = Range("A1")
  Call sample2(MyRange)
  MyRange.Value = 2
End Sub

Sub sample2(ByVal MyRange As Range)
  MyRange.Value = 1
  Set MyRange = Range("A2")
End Sub

上記の結果は、A1セルに2が入るだけです。
sample2での
Set MyRange = Range("A2")
これは、ByValなので呼出し元に影響を与えません。


オブジェクト変数がByRefの場合

Sub sample()
  Dim MyRange As Range
  Set MyRange = Range("A1")
  Call sample2(MyRange)
  MyRange.Value = 2
End Sub

Sub sample2(ByRef MyRange As Range)
  MyRange.Value = 1
  Set MyRange = Range("A2")
End Sub

この結果は、A1セルに1、A2セルに2が入ります。
sample2での
Set MyRange = Range("A2")
この変更が、呼出し元に返されています。

ここでは、とても単純な例にしましたが、
オブジェクトを引数に使う場合の参考にして下さい。


サイト内のByVal,ByRefの記事

Variant仮引数のByRefとByValの挙動違い
ツイッターで出したVBAのお題です。Variant型は、どんなデータ型も入れることができてしまいます。具体的なデータ型の代わりに使用することで、より柔軟にVBA記述ができるようになります。ただしこの便利さゆえに、逆に注意しなければならない挙動もあります。
オブジェクトのByRef、ByVal、Variant
ツイッターで出題した問題です。オブジェクトを引数にした場合の、ByRef、ByValの違いと、仮引数にVariantを指定した場合の動作違いを問う問題です。出題ツイート 【VBA問題】 Submain() Range("A1")=1 DimaAsRange:Seta=Range("A1…




同じテーマ「マクロVBA技術解説」の記事

WorksheetFunctionについて

・WorksheetFunction.VLookup ・WorksheetFunctionで使用できる関数 ・WorksheetFunctionの最後に
RangeとCellsの深遠
RangeとCells特集にします。今さら…と、あなどるなかれ、結構奥が深いのです。すでに説明した内容もありますが、知っておいた方が良い事、知らなくても困らない事(笑) これらを、まとめてみました。まずは基本 A1セルに"エクセル"と入れる場合。
Offset、Resizeを使いこなそう
・OffsetプロパティとResizeプロパティの構文 ・OffsetとResizeの基本 ・Offsetの注意点 ・OffsetとResizeの応用 ・OffsetやResizeでVBAが簡潔になる例
値渡し(ByVal)、参照渡し(ByRef)について
最終行・最終列の取得方法(End,CurrentRegion,SpecialCells,UsedRange)
・最終行取得の基本:手動ではCtrl + ↑、VBAではCells(1, 1).End(xlDown) ・最終列の取得 ・特殊な表の場合 ・CurrentRegion ・SpecialCells(xlCellTypeLastCell) ・UsedRange ・Findメソッド ・サイト内関連ページ
ユーザー定義関数の作り方
・簡単な例でユーザー定義関数を作ってみましょう ・この関数の使い方 ・ユーザー定義関数の実践使用例
セルの値について(Value,Value2,Text)
マクロVBAにおいて、セルの値を操作する事は基本の基本ですが、意外に理解できていない場合が多いようです。Value 指定されたセル範囲の値を表すバリアント型(Variant)の値を設定します。値の取得および設定が可能です。
Excelのバージョンを判断して「名前を付けて保存」
Excel2007以降とExcel2003以前ではExcelのファイルフォーマットが違います、Excel2007以降で2003以前の形式で保存したい場合に問題が発生します。Excel2007以降で普通に97-2003形式で保存するVBAコードを書くと、Excel2003以前ではエラーとなってしまいます。
空白セルを正しく判定する方法(IsEmpty,IsError,HasFormula)
・セルの値が空白の判定 ・計算式が入っていない判定 ・エラー値の判定 ・IsEmpty関数:空白を判定するVBA関数 ・TypeName関数:データ型を判定するVBA関数 ・RangeオブジェクトのFormulaプロパティ ・空白セルを正しく判定する方法続編
空白セルを正しく判定する方法2
空白セルの判定について、いろいろな方から意見を頂きました。やはり、空白判定は奥が深く結構難しいものとなっています。ここでは、各プロパティや関数が、セルの状態によって返す値を再確認してみます。元記事は、空白セルを正しく判定する方法(IsEmpty,IsError,HasFormula) 上記の記事では、
Rangeオブジェクト.Valueの省略について
エクセルVBAを教えていて、これほど多く聞かれる質問はないでしょう、RangeやCellsの.Valueは省略したほうが良いか、書いた方が良いか、当然、省略出来ない場合もあれば、オブジェクトとして扱うために.Valueは書けない場合もあります。ですので、結論から言えば、書きたければ書けば良いし、書きたくなければ書か…


新着記事NEW ・・・新着記事一覧を見る

ブール型(Boolean)のis変数・フラグについて|VBA技術解説(2024-04-05)
テキストの内容によって図形を削除する|VBA技術解説(2024-04-02)
ExcelマクロVBA入門目次|エクセルの神髄(2024-03-20)
VBA10大躓きポイント(初心者が躓きやすいポイント)|VBA技術解説(2024-03-05)
テンキーのスクリーンキーボード作成|ユーザーフォーム入門(2024-02-26)
無効な前方参照か、コンパイルされていない種類への参照です。|エクセル雑感(2024-02-17)
初級脱出10問パック|VBA練習問題(2024-01-24)
累計を求める数式あれこれ|エクセル関数応用(2024-01-22)
複数の文字列を検索して置換するSUBSTITUTE|エクセル入門(2024-01-03)
いくつかの数式の計算中にリソース不足になりました。|エクセル雑感(2023-12-28)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
3.RangeとCellsの使い方|VBA入門
4.ひらがな⇔カタカナの変換|エクセル基本操作
5.繰り返し処理(For Next)|VBA入門
6.変数宣言のDimとデータ型|VBA入門
7.ブックを閉じる・保存(Close,Save,SaveAs)|VBA入門
8.並べ替え(Sort)|VBA入門
9.セルのクリア(Clear,ClearContents)|VBA入門
10.Findメソッド(Find,FindNext,FindPrevious)|VBA入門




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。


このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
本文下部へ