LWP | 呼出先で変わる値をByRefとF8で追う
図解1|呼出先の代入が、元の値を変える

図1|図中の箱は意味を理解するモデルです。実際のメモリアドレスを示す図ではありません。
解説1|名前が違っても、元の変数を変更できる
ストーリー
照合担当のフナさんは、処理から戻った後に数値が変わっていることに気づきました。コイ丸先輩と、変更した行を呼出先まで追います。金額の複雑な計算はいったん外し、整数10を20へ変える最小の例で確かめます。
呼出元の変数名はx、呼出先が受け取る引数名はvalueです。名前が違っていても、ByRefを指定したLong型の引数へLong型の変数xを渡すと、valueへの代入によってxの値を変えられます。「呼出元にx=20が見当たらない」という理由だけでは、xが変わらないとはいえません。
サンプルコード|標準モジュールへ二つのSubを入力
01 |
|
|---|---|
02 |
|
03 |
|
04 |
|
05 |
|
06 |
|
07 |
|
08 |
|
09 |
|
10 |
|
練習用のマクロ有効ブックでAlt+F11を押し、VBEの[挿入]→[標準モジュール]へ上のコードを入れます。TakiLibなどの追加ライブラリは不要です。既存業務コードに同名のSubを足すのではなく、練習用の新しいモジュールで試します。
役割と結果
DemoByRefが呼出元、SetTwentyが呼出先です。Callは呼び出し、ByRefは引数の渡し方、Debug.Printはイミディエイト ウィンドウへの出力です。最後の出力は20になります。次のページでは、実行途中のどこで10から20へ変わるかを観察します。
図解2|呼び出す直前で、値を記録する

図2|黄色い実行位置は、これから実行する行です。
解説2|ブレークポイントで、変更前の状態を押さえる
デバッグでは「今どの処理を実行しようとしているか」と「ここまでの結果が何か」を分けて見ます。呼出行で停止した段階ではSetTwentyの中身はまだ実行されていません。xには、その前の行で代入された10が入っています。
止める場所と、値を見る場所
VBEで[表示]→[ローカル ウィンドウ]と[イミディエイト ウィンドウ]を表示します。ローカル ウィンドウは現在のプロシージャで見える変数を確認する場所です。イミディエイト ウィンドウには、後でDebug.Printの出力が表示されます。
1. DemoByRefの「Call SetTwenty(x)」の行にカーソルを置き、F9でブレークポイントを設定します。
2. DemoByRef内にカーソルがある状態でF5を押します。
3. 呼出行で停止したら、ローカル ウィンドウのxが10であることを確認します。
停止時に記録すること
対象 |
この時点の状態 |
|---|---|
現在のプロシージャ |
DemoByRef |
次に実行する行 |
Call SetTwenty(x) |
xの現在値 |
10 |
SetTwenty内の代入 |
まだ実行していない |
フナさん:「結果の20と、止まっている今の10は分けて記録するんですね。」コイ丸先輩:「そう。いつ変わったかが分かるようにしておこう。」
読むポイント
F9は停止位置を設定・解除するキー、F5は実行を進めるキーです。Dimや空行ではなく、実行する呼出行へブレークポイントを置きます。図は操作の意味を示す模式図です。機種によってはファンクションキーの入力にFnキーが必要です。
図解3|F8で中へ入り、代入の前後を見る

図3|黄色いvalue=20の行で止まっただけでは、代入はまだです。
解説3|代入を実行した直後に、変化を確かめる
呼出行で止まった状態からF8でステップ実行します。F8は呼出先のプロシージャ内へ入って追える操作です。宣言行などを経て進む場合もあるので、押した回数を暗記せず、黄色い行がどこにあるかを見てください。
観察する三つの時点
時点 |
次に実行する位置 |
確認する値 |
|---|---|---|
呼出前 |
Call SetTwenty(x) |
呼出元のx=10 |
代入前 |
value = 20 |
呼出先のvalue=10 |
代入後 |
End Subへ進んだ段階 |
呼出先のvalue=20 |
value=20の行が黄色いとき、ローカル ウィンドウのvalueはまだ10です。ここでF8を押し、代入文を実行すると20になります。続けて呼出元へ戻り、Debug.Print xを実行する直前に、xが20になったことを確かめます。
変数が見えなくなったとき
SetTwentyの中へ入ると、ローカル ウィンドウは呼出先の変数を表示します。xが一覧から消えたことを、変数の値が失われたと解釈しないでください。呼出先ではvalueを観察し、呼出元へ戻ってからxを確認すれば、この短い例の変化を追えます。
フナさん:「valueを20にした後で、戻ったxも20でした。」コイ丸先輩:「今回、元の値を変えたのは、その代入文だね。」
読むポイント
黄色い行の右辺に20と書いてあることと、現在値が20であることは別です。代入前と代入後を混同すると、「どの行が変えたのか」を一行ずらして判断してしまいます。まず停止位置、次に現在値、そして一文進めた後の値を見ます。
図解4|ByValなら、受け取った値のコピーを変更する

図4|比較する条件をそろえ、どちらもx=10から始めます。
解説4|呼出先を直す前に、意図した渡し方か確かめる
数値をByValで渡すと、呼出先は受け取った値のコピーを扱います。valueに20を代入しても、呼出元のxは10のままです。下の二つのSubを同じ標準モジュールへ追加し、DemoByValを別に実行して比べます。
サンプルコード|数値LongをByValで渡す比較
01 |
|
|---|---|
02 |
|
03 |
|
04 |
|
05 |
|
06 |
|
07 |
|
08 |
|
09 |
|
10 |
|
計算結果|それぞれ独立して実行
渡し方 |
呼出先で代入したvalue |
戻った後のx |
|---|---|---|
ByRef |
20 |
20 |
ByVal |
20 |
10 |
フナさん:「同じ20を代入しても、変更が届く先が違いますね。」コイ丸先輩:「元の値を変える必要があるのか、処理の目的から確認しよう。」
ByRef自体が不具合というわけではありません。呼出先で元の変数を更新する設計なら、変更は意図した動作です。読取り用のつもりだったなら、引数宣言と呼出先の代入を見直します。結果だけを合わせようと、理由なくByValへ変えるのは避けましょう。
この比較はLong型の数値変数に限定しています。オブジェクトをByValで渡した場合に、そのオブジェクトの中身まで複製されるという意味ではありません。
図解5|変更した行を、観測した値で説明する

図5|呼出先の代入文と、呼出元へ戻った値をつなげて説明します。
解説5|デバッグの結果を、再現できる記録にする
「ByRefだから変わった」で終えるより、どこで止まり、何を見たかを残すと、別の人も同じ結果を確かめられます。今回の記録は、呼出先のvalueへの代入が、呼出元のxを変えたことを示しています。
観測記録|DemoByRef
順番 |
観測する場所 |
現在値 |
|---|---|---|
1 |
呼出行の実行前 |
x=10 |
2 |
value=20の実行前 |
value=10 |
3 |
代入を実行した後 |
value=20 |
4 |
呼出元へ戻った直後 |
x=20 |
説明するなら「xを10で渡し、SetTwenty内のvalue=20を実行した後、呼出元でx=20を確認した」と書けます。変数名・プロシージャ名・代入行・前後の値を含めることが要点です。
操作の後始末
確認が終わったらF5で残りを実行して終了し、使った呼出行のブレークポイントはF9で解除します。イミディエイト ウィンドウには以前の出力も残るため、新しく実行した結果と過去の結果を混同しないでください。
フナさん:「次は金額が変わる処理も、この順で調べられそうです。」コイ丸先輩:「まず変わる前後を絞ろう。全部の行を最初から追う必要はないよ。」
まとめ・今回の仕掛け
数値の計算を10→20だけにして、渡し方による違いを見えるようにしました。停止位置と現在値を分け、呼出先へ入り、代入を一文進め、戻った値を照合します。ByRefとByValの比較は同じ初期値で行います。
出典と確認範囲
記事元ネタ「参照とは何か_VBA_ByRef」の数値例を独立記事へ再構成。Microsoft:引数を効率的に渡す、Debug menu。2026-10-13確認。掲載コードの実行結果と変更前後をExcelで検証。F8・F9の画面操作自体は追試せず、公式仕様と照合しています。
