t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBA なぜParamArrayは参照渡しできないのか

今回はタイトルの問いに対し、根本回答が見つかっていない。
一応、分かる範囲で検証してみたのでその結果報告である。

背景

mmYYmmddさんがメモリ効率に関して記事を書いてくれたので確認。
qiita.com

なるほど、C++を経由してポインタでSwapする手があった。
しかし外部DLLになってしまうため、メモリ効率とはトレードオフである。

さて、上記の記事にも書かれているように、ParamArrayは他の関数に参照渡しできない。
普通の配列と同じように扱えるのに、実際のところどう違うのか謎である。

検証

ということで調べてられる範囲でその正体に迫ってみた。
ローカルウインドウを表示させて次のtestマクロを実行する。

Sub test()
    Dim a, b, c
    a = "aaaa"
    b = 2222
    c = "cccc"
    Call 配列チェック(a, b, c)
End Sub


Sub 配列チェック(ParamArray パラメーター配列() As Variant)
    Dim バリアント配列(2)
    バリアント配列(0) = "aaaa"
    バリアント配列(1) = 2222
    バリアント配列(2) = "cccc"
    
    Dim コピー配列: コピー配列 = パラメーター配列
    
    Dim 普通の配列(2) As String
    Debug.Assert False  'BreakPointの代わり
End Sub

すると結果は次のとおり。
f:id:t-hom:20150704212912p:plain

パラメーター配列は確かに普通の配列と構造が違う。
バリアント型は内部に型スイッチを持っていて、一応入っているデータが何であるのかを識別しているようだ。

たとえばVariant/Stringなら、バリアント型で内部タイプが文字列ということ。
だからTypeName関数に文字列タイプのバリアント型変数を入れると、ちゃんと"String"と返ってくる。

【参考】
VARIANT型を知ってみる
なお、バリアントのデータ部分はCの共用体が使われているようだ。(ここで初めて共用体の存在意義を理解した。)


さて、パラメーター配列(0)の型を見ると、Variant型のネストが発生しているように見える。
これが他の関数に参照渡しできない原因なんだろうか。

ちなみに、値渡しすると、受け取り側では構造が変わるようだ。
f:id:t-hom:20150704220048p:plain

変数に代入した場合(先の例ではコピー配列)と同じ構造になっている。

ParamArrayでは複数パラメーターを受け取るために設計上バリアントのネストが発生してしまったのだろうとは想像できるけれど、じゃあ他の変数に代入したり値渡しすると型の構造が変わるのはなぜか分からない。

使う方はとりあえず、ParamArrayは参照渡しできないという仕様だけ把握していれば良いと思う。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。