TL;DR — VBA の配列は、角かっこを空にしておかないかぎり、
Dimした瞬間にサイズが固定されます。Dim a()はまだサイズのない 動的 配列を宣言し、ReDim a(1 To 10)がそこにサイズを与えます。素のReDimは保持していた値をすべて捨ててしまうので、データを失わずに配列を伸ばすにはReDim Preserveを使います。落とし穴はこうです。二次元配列では、ReDim Preserveは 最後の 次元しかリサイズできませ ん。固定サイズの配列(Dim a(1 To 10))は、そもそも一度もReDimできません。
Sub GrowAnArray()
Dim a() As Long ' 動的 - まだサイズがない
ReDim a(1 To 3) ' これで Long を 3 個持つ
a(1) = 10: a(2) = 20: a(3) = 30
ReDim Preserve a(1 To 5) ' 5 個に伸ばす、10, 20, 30 は残す
a(4) = 40: a(5) = 50 ' ここで素の ReDim なら 10-30 が消えていた
End Sub
ReDim は 動的 配列の物語そのものです。配列を Dim a() で開いたまま宣言し、
ReDim でサイズを与え、UBound でどれだけの大きさになったかを尋ね、そして —
グリッドなら — 伸ばせるのは最後の次元だけだと覚えておきます(VBA 2D Arrays を参
照)。ReDim を間違えるとデータは静かに消えてしまうので、サイズが いつ 固定され、いつそうでないのかを
理解しておく価値があります。
この記事で学べること
- すべての配列を左右する唯一の判断 —
Dimで固定サイズにするか、Dim a()で開いておくか ReDimが動的配列にサイズを与えるしくみ、そして 素のReDimが保持していたものをすべて消す理由ReDim Preserve— 配列を伸ばしつつデータを保つ方法- 第一の罠 —
ReDim Preserveは二次元配列の 最後の 次元しかリサイズできない - 固定サイズの配列を
ReDimできない理由、そしてEraseが実際に何をするのか - ループの中で配列を一行ずつ伸ばすとなぜ遅いのか、そして代わりにどうすべきか
考え方の軸:Dim で固定するか、空の角かっこで開くか
VBA の配列は二種類のどちらかであり、どちらにするかは宣言時に選びます。
Dim fixed(1 To 10) As Long ' 固定 - 10 個の枠、ずっと変わらない; サイズは型の一部
Dim flex() As Long ' 動的 - まだサイズがない; あとで ReDim する
固定サイズ の配列は一度だけサイズが決まり、二度と変わりません — サイズが宣言の中に宿っているので、VBA
はあとから ReDim させてくれません(コンパイルエラー Array already dimensioned が出ます)。動的 配
列は空の角かっこで宣言します。型はあってもサイズはなく、ReDim こそがそれにサイズを与える手段です。この
一つの選択 — Dim の時点で角かっこを埋めるか空にするか — が、このページの残りすべてを決めます。いくつ項
目を持つことになるかまだ分からないなら、開いたまま宣言しましょう。
ReDim は動的配列にサイズを与える
ReDim は動的配列に新しい記憶領域を割り当てます。同じ配列を好きなだけ ReDim でき、それぞれの ReDim
は上限・下限すら変えられます。
Dim a() As String
ReDim a(1 To 3) ' 3 要素、添字は 1..3
ReDim a(0 To 99) ' これで 100 要素、添字は 0..99
頭に入れておくべきことが一つあり、それが「データが消えた」というバグのほとんどの原因です。素の ReDim
は 配列全体を初期化し直します。すべての要素が既定値へ戻り — 数値なら 0、文字列なら ""、オブジェク
トなら Nothing — まるで今作ったばかりのようになります。
Dim a() As Long
ReDim a(1 To 3)
a(1) = 10: a(2) = 20: a(3) = 30
ReDim a(1 To 5) ' a(1..3) はまた 0 に戻る - 値は消えた
データを残すつもりだったなら、まさにそのために Preserve があります。
ReDim Preserve:データを失わずに伸ばす
ReDim Preserve は、リサイズした配列を返す前に、既存の値をその中へコピーします。
Dim a() As Long
ReDim a(1 To 3)
a(1) = 10: a(2) = 20: a(3) = 30
ReDim Preserve a(1 To 5) ' a(1..3) はまだ 10, 20, 30 を保持; a(4), a(5) は 0
すでに埋めた配列を拡張するときはいつでも Preserve を使いましょう。伸ばすときも 縮める ときも動きます
— 縮める場合は、新しい上限を超えた要素が単に捨てられるだけです。守らなければならない唯一の規則は次元につ
いてのもので、そこでほとんど誰もがつまずきます。
第一の罠:Preserve は最後の次元しかリサイズできない
一次元配列では、ReDim Preserve は上限・下限を自由に変えられます。二次元 配列では、ReDim Preserve
は 最後の 次元しか変えられません。最初の次元を変えようとすると、VBA は実行時エラー 9、
Subscript out of range を発生させます。
Dim g() As Long
ReDim g(1 To 3, 1 To 2) ' 3 行、2 列
ReDim Preserve g(1 To 5, 1 To 2) ' エラー 9 - 最初の次元はリサイズできない
ReDim Preserve g(1 To 3, 1 To 4) ' OK - 最後の(列の)次元は伸ばせる
これは、もっと巧妙な ReDim で回避できるバグではありません。VBA がメモリ上で配列をどう格納するか、その
ものだからです。直感にも反します。シートは 行 × 列なので、たいてい欲しい「伸ばす」— 行を足すこと — は
最初の次元、つまり触れてはいけない次元にあたるからです。抜け道は二つ。グリッドを 列 × 行として宣言し、
伸ばす次元を最後に持ってくるか、あるいはもっと一般的には、ループの前に配列を最終的な行数へ一度だけサイズ
指定し、Preserve はいっさい使わないことです。既存の二次元配列にどうしても行を足さなければならないなら、
作り直します(あるいは転置し、最後の次元をリサイズし、また転置し戻します)。詳しくは
VBA 2D Arrays にあります。
固定サイズの配列は ReDim できない
ReDim は空の角かっこで宣言した配列専用です。Dim の時点で配列にサイズを与えたなら、そのサイズは宣言に
焼き込まれ、ReDim はコンパイルエラーになります。
Dim a(1 To 10) As Long
ReDim a(1 To 20) ' コンパイルエラー - "Array already dimensioned"
配列をリサイズしたくなったとしたら、それはそもそも動的(Dim a())として宣言すべきだった、という合図で
す。個数が分かった直後に一度だけ ReDim する動的配列は、両方のいいとこ取りを与えてくれます。実行時に選
んだ、すっきりした固定の作業用サイズです。
Erase:初期化するか、解放するか
Erase は、配列の種類によって二つの異なることをします。固定サイズ の配列では、すべての要素を既定値へ
戻しますが、配列のサイズはそのまま保ちます。動的 配列では、さらに踏み込みます — メモリを解放し、まる
で Dim a() としか書いていなかったかのように、配列をサイズのない状態へ戻します。
Dim fixed(1 To 3) As Long
Erase fixed ' 3 要素すべてがまた 0 に; サイズは 3 のまま
Dim flex() As Long
ReDim flex(1 To 3)
Erase flex ' メモリが解放される; flex はまたサイズなし - 再利用の前に ReDim
動的配列を Erase したあとは、もう一度 ReDim するまで、それに対して UBound を呼
ぶと実行時エラー 9 が出ます — 配列には本当に報告すべき上限・下限がないのです。
一度でサイズを決め、一つずつ伸ばさない
ReDim Preserve は毎回、新しいブロックを割り当てて古い中身をそこへコピーするので、ループの一周ごとに配
列を一つずつ伸ばすと、O(n) の仕事が O(n²) になってしまいます — 50 項目なら問題ありませんが、50,000 項目
ではつらくなります。定石の直し方は、すでに手元にある個数(最終行、コレクションのサイズ)を使って、最初に
一度だけ配列をサイズ指定し、必要なら最後に切り詰めるだけにすることです。
Dim a() As Long, n As Long, i As Long
n = Cells(Rows.Count, 1).End(xlUp).Row ' データの行数
ReDim a(1 To n) ' 割り当ては一度だけ
For i = 1 To n
a(i) = Cells(i, 1).Value
Next i
個数を事前に本当に知りようがないときの定番は、大きめのかたまりで ReDim し(たとえば足りなくなったらサ
イズを倍にする)、最後に一度だけ正確な個数まで ReDim Preserve で縮めることです — 毎回一つずつ伸ばすよ
りコピーの回数がはるかに少なくて済みます。
ExcelMaster の活用
ReDim をめぐる話はすべて、一つの判断に尽きます — この配列は固定サイズか、それとも伸びるのか? — そして
典型的な失敗は二つ、データを静かに消してしまう素の ReDim と、間違った次元で実行時エラー 9 にぶつかる
ReDim Preserve です。
ExcelMaster には、やりたいことをそのま
ま伝えられます — 「A 列を配列に読み込んで、空白でない値だけ残して、C 列に書き出して」 — すると、配列を動
的に宣言し、実際の行数から一度だけサイズを決め、次元が許す場所でだけ ReDim Preserve を使い、セルを一つ
ずつではなく範囲を一度で読み取ります。ブックとコードはあなたの手元に残ります。
よくある質問
VBA の ReDim は何をするのですか?
ReDim は動的配列 — Dim a() As Long のように空の角かっこで宣言した配列 — のサイズを設定、または変更し
ます。ReDim a(1 To 10) は配列に 10 要素を与えます。同じ配列を繰り返し ReDim でき、それぞれの ReDim
で上限・下限を変えられます。素の ReDim はすべての要素を既定値へ初期化し直しもするので、既存のデータを
保つ必要があるときは ReDim Preserve を使いましょう。
ReDim と ReDim Preserve の違いは何ですか?
素の ReDim は新しいサイズを割り当て、すべての要素を既定値へ戻し、配列が保持していたものを捨てます。
ReDim Preserve は先に既存の値をリサイズ後の配列へコピーするので、データは生き残ります。最初のサイズ指
定には素の ReDim を、すでに埋めた配列を拡張するときはいつでも ReDim Preserve を使いましょう。
二次元配列で ReDim Preserve が実行時エラー 9 を出すのはなぜですか?
ReDim Preserve は多次元配列の 最後の 次元しかリサイズできないからです。最初の次元を変えると、実行時
エラー 9、Subscript out of range が発生します。グリッドに「行」を足すには、列 × 行として宣言して伸びる
次元を最後にするか、ループの前に最終的な行数へサイズ指定するか、配列を作り直しましょう。
固定サイズの配列を ReDim できますか?
いいえ。Dim a(1 To 10) のようにサイズ付きで配列を宣言したなら、そのサイズは型の一部であり、ReDim は
コンパイルエラー Array already dimensioned になります。ReDim でサイズを与えたりリサイズしたりできるの
は、空の角かっこ(Dim a())で宣言した配列だけです。
VBA の配列に要素を追加するには?
個数を自分で管理し、ReDim Preserve で場所を空けます。ReDim Preserve a(1 To n + 1): a(n + 1) = value: n = n + 1。これで動きますが、要素ごとにリサイズするのは遅いです。ReDim Preserve は毎回、配列全体をコピ
ーするからです。大きなループでは、分かっている個数から一度だけサイズを決めるか、かたまりで伸ばして最後に
一度切り詰めましょう。個数が本当に不明なときは、Collection のほうが簡単なことが多いです。
検証環境
動作確認: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-09-24.
関連ガイド: VBA UBound · VBA 2D Arrays · VBA Array · VBA Dim · VBA For Loop
