VB
VBA
マクロ
動的配列
Excel VBADay 18

なぜVBAの動的配列は最後の次元の配列数しか変えられないのか考えてみた

いままで、動的配列が最後の次元の配列数しか変えられないことに何の疑問も持っていませんでした。
しかし、このたび、VBA Adventcaledar 2017の12月3日のmad_nectarineさんの投稿「Excel VBAでCognitive Service使って翻訳してみた 〜VBA未経験なWebアプリ開発者が2日間VBAと戯れた結果 ~」を拝見して、そういえばどうしてなんだろうと思い、考えてみました。

たぶん、配列の内部構造に理由があることは間違いないと思うので、2x3次元の配列を確保して、メモリ内での配置をみてみたいと思います。
通常、メモリ内での変数の位置は、16進数で表すことが一般的だと思いますが、相対的な位置関係を見たいので、10進数でみることにします。

プログラムは以下のプログラムを書いてみました。

Sub Main()
    Dim a() As Long
    ReDim a(0 To 1, 0 To 2)

    Debug.Print 0, 0, VarPtr(a(0, 0))
    Debug.Print 0, 1, VarPtr(a(0, 1))
    Debug.Print 0, 2, VarPtr(a(0, 2))
    Debug.Print 1, 0, VarPtr(a(1, 0))
    Debug.Print 1, 1, VarPtr(a(1, 1))
    Debug.Print 1, 2, VarPtr(a(1, 2))
End Sub

結果は、こうなりました。
image.png
Long型は4バイト変数なので、メモリ上での位置は4ずつずれていくんだと思ってましたが、これをみるとそうではないようです。
下二桁に注目して、メモリ上での配置を図にしてみます。
image.png
なるほど、a(0,0)の隣に、a(0,1)がいるかと思ったら、a(1,0)がいるんですね。
最後の次元が同じもの同士を隣同士に固めて配置しているようです。

この配置であれば、最後の添え字数を変更するだけであれば、内部処理としては、添え字数を変更しても、再配置処理は単純なコピーで済むので、高速に処理できそうです。
これが理由なのでしょう。
理解を助けるために、ためしに、配列数を変更して試してみます。

Sub Main()
    Dim a() As Long
    ReDim a(0 To 1, 0 To 2)

    Debug.Print 0, 0, VarPtr(a(0, 0))
    Debug.Print 0, 1, VarPtr(a(0, 1))
    Debug.Print 0, 2, VarPtr(a(0, 2))
    Debug.Print 1, 0, VarPtr(a(1, 0))
    Debug.Print 1, 1, VarPtr(a(1, 1))
    Debug.Print 1, 2, VarPtr(a(1, 2))

    Debug.Print "---------------"
    ReDim a(0 To 1, 0 To 4)

    Debug.Print 0, 0, VarPtr(a(0, 0))
    Debug.Print 0, 1, VarPtr(a(0, 1))
    Debug.Print 0, 2, VarPtr(a(0, 2))
    Debug.Print 0, 3, VarPtr(a(0, 3))
    Debug.Print 0, 4, VarPtr(a(0, 4))
    Debug.Print 1, 0, VarPtr(a(1, 0))
    Debug.Print 1, 1, VarPtr(a(1, 1))
    Debug.Print 1, 2, VarPtr(a(1, 2))
    Debug.Print 1, 3, VarPtr(a(1, 3))
    Debug.Print 1, 4, VarPtr(a(1, 4))

End Sub

結果はこうなりました。
image.png

下3ケタに注目して、図にしてみます。
初期状態
image.png

Redim Preserve後
image.png

A(0,0)からa(1,2)までの部分の相対的な位置関係はRedimPreserveする前と変わりがなく、新たに後ろに、a(0,3),a(1,3),a(0,4),a(1,4)を追加しています。
配列数の変更をする際の内部処理を簡略にして、高速に処理することが、VBAの動的配列の仕様の狙いなんだろうなと思いました。
VBAはただでさえ、遅い遅いとC++派なプロプログラマーに馬鹿にされるので、高速化はうれしいところですが、VBAがプロ用の言語でないところを考えると、こういうわかりにくい仕様って、どうなんだろうとも思ったりします。