TL;DR — VBA では、配列を 数える のではなく、配列に 尋ねます。
UBound(a)は有効な最大の添字を 返し、LBound(a)は最小の添字を返します。配列は 既定で 0 始まり なので、Dim a(9)は添字0から9までの 10 要素を持ちます —UBoundは10ではなく9です。この 1 つのずれが、ループで最もよく 出るバグです。いつでも生き残るループはFor i = LBound(a) To UBound(a)であり、本当の要素数はUBound(a) - LBound(a) + 1です。配列に.Lengthや.Countはありません。
Sub LoopSafely()
Dim a() As String
a = Split("red,green,blue", ",") ' Split は 0 始まりの配列を返す
Dim i As Long
For i = LBound(a) To UBound(a) ' 上限・下限が何であれ動く
Debug.Print i, a(i) ' 0 red / 1 green / 2 blue
Next i
Debug.Print "count = " & (UBound(a) - LBound(a) + 1) ' 3
End Sub
UBound は配列の形を読み取る手段であり、ReDim の相棒です。配列にサイズを与え、そ
れからどんなサイズになったかを尋ねます。上限・下限を間違えると、最後の要素を飛ばすか、末尾を越えて実行時
エラー 9 に突っ込むかのどちらかです。配列を渡してくる相手によって開始の添字が違うので、0 や 1 だと決
めてかからず 尋ねる 習慣こそが、ループを正しく保ちます。
この記事で学べること
UBoundとLBoundが何を返すのか、そして配列に.Lengthプロパティがない理由- VBA の配列が既定で 0 始まりである理由、そして 1 つずれるバグがどこから来るのか
- いつでも動くループ —
For i = LBound(a) To UBound(a) - 本当の配列の要素数 —
UBound(a) - LBound(a) + 1 - 二次元配列の各次元のサイズを
UBound(a, 2)で得る - 空の動的配列に
UBoundを使うとなぜ実行時エラー 9 になるのか、そしてそれをどう守るか
UBound と LBound:数えるのではなく、尋ねる
UBound(上限)は合法に使える最大の添字を返し、LBound(下限)は最小の添字を返します。二つ合わせて配列
の形を表します。VBA の配列には、あえて .Length も .Count も ありません — 上限・下限こそが正しさの
よりどころであり、それを実行時に読み取ります。
Dim a(1 To 5) As Long
Debug.Print LBound(a) ' 1
Debug.Print UBound(a) ' 5
なぜ覚えておくのではなく尋ねるのか? ループで回している配列は、たいてい別のどこかでサイズが決められている
からです — ReDim で、Split で、範囲を読み込んで — そして自分が そうだと思って
いる サイズを直書きすることこそ、ループが現実とずれていく原因なのです。
既定で 0 始まり:1 つずれが潜む場所
特に指定しなければ、VBA の配列は添字 0 から始まります。ですから Dim a(9) は 9 要素を与えるのでは
ありません — 添字 0 から 9 までの 10 要素を与えます。
Dim a(9) As Long ' 10 要素: a(0) .. a(9)
Debug.Print LBound(a) ' 0
Debug.Print UBound(a) ' 9 <- 10 ではない
これが典型的なバグの根っこです。For i = 1 To UBound(a) は a(0) を黙って飛ばし、For i = 0 To 10 は一
歩行き過ぎて Subscript out of range にぶつかります。Option Base 1 でモジュール全体を 1 始まりに強制し
たり、Dim a(1 To 10) で明示的に上限・下限を宣言したりもできますが — 本当の直し方は、開始をそもそも推測
するのをやめることです。
いつでも動くループ
ループを配列自身の上限・下限で書けば、配列がどう宣言されたか、どこから来たかによらず正しくなります。
Dim i As Long
For i = LBound(a) To UBound(a)
' ... a(i) を使う ...
Next i
この一つの習慣が、1 つずれるエラーの一族をまるごと取り除きます。0 始まりの Split の結果でも、1 始まりの
Range.Value 配列でも、Option Base 1 のモジュールでも、明示的な Dim a(5 To 12) でも等しく動きます。
LBound がただで本当のことを教えてくれるのに、配列ループの開始に 0 や 1 を直書きしてはいけません。
配列の要素数:UBound から LBound を引いて 1 を足す
配列はどんな添字からでも始まれるので、その要素数は単なる UBound + 1 ではありません。いつでも正しい式は
これです。
Dim n As Long
n = UBound(a) - LBound(a) + 1
10 要素の 0 始まり配列なら 9 - 0 + 1 = 10、Dim a(1 To 10) なら 10 - 1 + 1 = 10 です。この完全な式を
使えば、配列がどの基点を使っていても関係ありません。(「VBA の配列の要素数」を返すプロパティを探す人がい
ますが、そんなものはありません — この式が答えです。)
二次元配列:次元ごとに上限・下限がある
多次元配列では、UBound と LBound は二つめの引数 — どの次元について尋ねているか — を取ります。
UBound(a, 1) は最初の次元(行)、UBound(a, 2) は二つめの次元(列)です。
Dim g(1 To 3, 1 To 4) As Long ' 3 行、4 列
Debug.Print UBound(g, 1) ' 3 (行)
Debug.Print UBound(g, 2) ' 4 (列)
Dim r As Long, c As Long
For r = LBound(g, 1) To UBound(g, 1)
For c = LBound(g, 2) To UBound(g, 2)
' ... g(r, c) を使う ...
Next c
Next r
次元番号のない素の UBound(g) は、最初の次元を返します。これが最も効いてくるのは、シートを配列に読み込
むときです。Range.Value はつねに 1 始まりの二次元配列を渡してくるので —
UBound(a, 1) と UBound(a, 2) を尋ねることが、実際に何行何列を受け取ったかを知る方法になります。
空の動的配列を守る
宣言されたものの一度も ReDim されていない動的配列(あるいは Erase された配列)には、上限・下限がま
ったくありません。それに UBound を呼ぶと、実行時エラー 9、Subscript out of range が発生します。
Dim a() As Long
Debug.Print UBound(a) ' エラー 9 - 配列にはまだサイズがない
配列が空である可能性が少しでもあるなら — たとえば何にも一致しなかった Filter から来た場合など — ループ
の前に守りましょう。小さなヘルパーを使うと、呼び出し側のコードがすっきりします。
Function IsAllocated(a As Variant) As Boolean
On Error Resume Next
IsAllocated = (LBound(a) <= UBound(a))
On Error GoTo 0
End Function
あとは If IsAllocated(a) Then For i = LBound(a) To UBound(a) ... です。これは、オブジェクトを使う前に
Is Nothing で守るのと、配列における同類です。
ExcelMaster の活用
UBound は小さな関数ですが、一つの大きな仕事を持っています — ループにどこで止まればいいかを正確に教える
ことです — そしてそのまわりの誤り(0 や 1 だと決めてかかる、配列が空だと忘れる、要素数を直書きする)
こそ、マクロが最後の行を飛ばしたり、境界のケースでクラッシュしたりする原因なのです。
ExcelMaster には、やりたいことをそのま
ま伝えられます — 「使用範囲を配列に取り込んで、各列を合計して、その合計を下に書き出して」 — すると、正し
い次元で LBound から UBound までループし、要素数を正しく計算し、空で返ってきたかもしれない配列を守る
ので、マクロは最初の行でも、最後の行でも、空のケースでも正しく動きます。ブックとコードはあなたの手元に残
ります。
よくある質問
VBA の UBound は何をするのですか?
UBound は配列の有効な最大の添字 — 角かっこの中で使える最大の数 — を返します。Dim a(1 To 5) なら
UBound(a) は 5、0 始まりの Dim a(9) なら 9 です。最小の添字を返す LBound と組み合わせれば、サイ
ズを直書きせずに配列をループできます。
VBA で配列の要素数を得るには?
VBA の配列に .Length や .Count プロパティはありません。要素数は UBound(a) - LBound(a) + 1 で求めま
す。VBA の配列は添字 0、1、あるいは任意の明示的な下限から始まれるので、完全な式が必要です — UBound + 1
が正しいのは 0 始まりの配列だけです。
UBound で Subscript out of range が出るのはなぜですか?
たいていは配列が空だからです — Dim a() で宣言したものの一度も ReDim していない動的配列や、Erase さ
れた配列には上限・下限がないので、UBound は実行時エラー 9 を発生させます。ループの前に、小さな
IsAllocated ヘルパーで守りましょう。ループが UBound を越えて走るときにも実行時エラー 9 は出ます。たと
えば 10 要素の 0 始まり配列に対する For i = 0 To 10 です。
VBA の配列は 0 始まりですか、1 始まりですか?
既定では 0 始まりです。Dim a(9) は添字 0 から 9 までの 10 要素を持ちます。Option Base 1 でモジュー
ルを 1 始まりにしたり、Dim a(1 To 10) で明示的に上限・下限を設定したりできます。Split や Array が返
す配列は 0 始まり、Range.Value から読んだ配列は 1 始まりです。まちまちなので、決めてかからず LBound
から UBound までループしましょう。
VBA で二次元配列のサイズを調べるには?
次元番号を二つめの引数として渡します。最初の次元(行)には UBound(a, 1)、二つめの次元(列)には
UBound(a, 2)、下限にはそれぞれ LBound(a, 1) と LBound(a, 2) です。これが、Range.Value が返す二次元
配列の形を読み取る方法です。
検証環境
動作確認: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-09-24.
関連ガイド: VBA ReDim · VBA 2D Arrays · VBA Array · VBA For Loop · VBA Split
