【2026年8月最新】VBA配列のUBound・LBound完全ガイド|要素数取得・列挙・エラー対処まで
「配列に何個データが入っているか、コードの中でどう調べればいいんだろう」——Excel VBAでマクロを組んでいて、この壁にぶつかった経験がある方は多いはずです。
配列の要素数を扱う場面で必ず登場するのがUBound関数とLBound関数です。名前だけ見ると難しそうですが、仕組みを理解すれば「配列の上限・下限のインデックスを返すだけ」のシンプルな関数です。ただし、多次元配列や動的配列が絡んでくると、途端に理解が怪しくなる方が多いのも事実です。
この記事を最後まで読むと、次の7つが明確になります。
01 BASICS UBound・LBoundとは何か 配列の基本構造から関数の役割まで
UBound・LBoundを理解するには、まず配列(はいれつ)そのものの構造を押さえておく必要があります。
📚 用語解説
配列(はいれつ):複数のデータを1つの変数名でまとめて管理する仕組み。例えば「Data(0)、Data(1)、Data(2)」のように、同じ変数名に番号(インデックス)をつけて複数の値を格納できます。Excelのセル範囲をまとめて処理したいときによく使われます。
配列には、要素を指す番号としてインデックスが振られます。VBAの配列は初期設定では0から始まる(0-based)ため、「Data(0)」が1つ目の要素になります。ただし、Option Base 1を宣言したり、後述のTo構文で明示的に下限を指定すると、1から始まる配列も作れます。
1-1. UBound関数とは
UBound関数は、配列の最大インデックス(上限)を返す関数です。「Upper Bound(上限)」の略で、配列に何番目の要素まで入っているかを調べるときに使います。
基本構文は次のとおりです。
UBound(配列名, [次元])
第2引数の「次元」は、多次元配列を扱うときにだけ指定します。1次元配列であれば省略可能で、その場合は自動的に1次元目が対象になります。
1-2. LBound関数とは
LBound関数は、UBoundとちょうど逆で、配列の最小インデックス(下限)を返します。「Lower Bound(下限)」の略です。
📚 用語解説
UBound / LBound:UBoundは配列の最大インデックス(上限)、LBoundは最小インデックス(下限)を返す関数。多くのVBA配列は下限が0のため見落とされがちですが、Excelのセル範囲を配列に変換した場合など、下限が1や別の数値になるケースもあるため、両方セットで確認する習慣が重要です。
構文はUBoundと全く同じ形です。
LBound(配列名, [次元])
「配列の下限は基本0だから、LBoundは不要では?」と思うかもしれません。しかし、Excelのセル範囲を配列に代入した場合(例: Range("A1:A10").Value を配列に格納)は下限が1になります。下限を決め打ちせず、常にLBoundで確認するクセをつけておくと、後々のバグを防げます。
02 1D ARRAY 1次元配列でのUBound・LBoundの使い方 最も基本的なパターンをコード例で確認する
まずは最も基本的な、1次元配列でのUBound・LBoundの使い方を見ていきます。
2-1. 静的配列でのサンプルコード
Sub Sample1D()
Dim Data(4) As String
Data(0) = "田中"
Data(1) = "佐藤"
Data(2) = "鈴木"
Data(3) = "高橋"
Data(4) = "伊藤"
Debug.Print "下限: " & LBound(Data) '結果: 0
Debug.Print "上限: " & UBound(Data) '結果: 4
End Sub
Dim Data(4)と宣言すると、インデックス0〜4の合計5個の箱が用意されます。この場合、LBoundは0、UBoundは4を返します。「宣言時のカッコの数字=要素数」ではない点が、初学者がつまずきやすいポイントです。
Dim Data(4)としたとき、多くの人が「要素数は4」と思い込みがちですが、実際は0番目から4番目までの5個です。UBoundの戻り値をそのまま要素数だと誤解すると、ループ処理や集計で1個ズレるバグの原因になります。
2-2. 下限を1から始める場合(To構文)
To構文を使うと、下限を任意の数値に指定できます。
Sub Sample1DFrom1()
Dim Data(1 To 5) As String
Data(1) = "田中"
Data(2) = "佐藤"
Data(3) = "鈴木"
Data(4) = "高橋"
Data(5) = "伊藤"
Debug.Print "下限: " & LBound(Data) '結果: 1
Debug.Print "上限: " & UBound(Data) '結果: 5
End Sub
「請求書の明細を1行目から数えたい」など、業務上1から数えたい場面では、この1 To 5のような書き方が重宝します。Excelの行番号감覚と一致させられるため、集計コードが直感的になります。
03 MULTI-DIM ARRAY 多次元配列でのUBound・LBoundの使い方 第2引数「次元」の指定を正しく理解する
Excelの表(行×列)のようなデータを扱う場合、多次元配列を使います。多次元配列でのUBound・LBoundは、第2引数の「次元」指定がポイントになります。
📚 用語解説
多次元配列:複数の添字(インデックス)を持つ配列。例えば「Data(行, 列)」のように2つの添字を持つものを2次元配列と呼び、Excelの表形式データ(縦×横)をそのまま格納するのに向いています。3次元以上の配列も定義できますが、実務では2次元配列が最も多く使われます。
3-1. 2次元配列のサンプルコード
Sub Sample2D()
Dim Data(2, 3) As String '3行×4列
Debug.Print "1次元目の下限: " & LBound(Data, 1) '結果: 0
Debug.Print "1次元目の上限: " & UBound(Data, 1) '結果: 2
Debug.Print "2次元目の下限: " & LBound(Data, 2) '結果: 0
Debug.Print "2次元目の上限: " & UBound(Data, 2) '結果: 3
End Sub
第2引数に1を指定すると1次元目(多くの場合「行」に相当)、2を指定すると2次元目(「列」に相当)の上限・下限が返ります。次元の指定を省略すると自動的に1次元目が対象になるため、2次元目を調べたい場合は必ず引数を明記する必要があります。
3-2. 3次元配列のサンプルコード
Sub Sample3D()
Dim Data(1, 2, 3) As Variant
Debug.Print UBound(Data, 1) '結果: 1
Debug.Print UBound(Data, 2) '結果: 2
Debug.Print UBound(Data, 3) '結果: 3
End Sub
3次元以上になると管理が複雑になるため、実務では「支店別×月別×商品別」のような3軸データを扱うケース以外では、あまり出番がありません。可読性を優先するなら、Collectionオブジェクトや辞書(Dictionary)への置き換えも検討する価値があります。
多次元配列でUBoundの第2引数を書き忘れると、常に1次元目の値が返ってきます。2次元目のループ処理で「なぜか3行分しか処理されない」といったバグの多くは、この指定漏れが原因です。多次元配列を扱うコードでは、UBound・LBoundの引数を必ず目視確認する習慣をつけてください。
04 COUNT ELEMENTS 配列の要素数を正しく取得する UBoundだけでは要素数にならない理由
ここが今回の記事で最も重要なポイントです。「配列の要素数」=「UBoundの戻り値」ではありません。正しい要素数の計算式は次のとおりです。
要素数 = UBound(配列) - LBound(配列) + 1
下限が0でも1でも、この式なら常に正確な要素数が求まります。
下限が0の配列であれば「UBound + 1」で足りると考えがちですが、この考え方は下限が1やそれ以外の値になったときに崩れます。常に「UBound - LBound + 1」の形で書いておけば、下限がいくつであっても正確な要素数を取得できます。
4-1. 要素数取得のサンプルコード
Sub CountElements()
Dim Data(1 To 5) As String
Dim cnt As Long
cnt = UBound(Data) - LBound(Data) + 1
Debug.Print "要素数: " & cnt '結果: 5
End Sub
文字列を区切って配列化するSplit関数の結果に対しても、この計算式は同じように使えます。
Sub CountSplitElements()
Dim arr() As String
arr = Split("東京,大阪,名古屋,福岡", ",")
Debug.Print "要素数: " & (UBound(arr) - LBound(arr) + 1) '結果: 4
End Sub
要素数を正しく求めるまでの考え方を図解すると、以下のような流れになります。
配列の下限を
LBoundで確認
配列の上限を
UBoundで確認
上限-下限+1で
要素数を計算
ループ処理・
集計に利用
05 ENUMERATION For文・For Eachで配列要素を列挙する 安全にループ処理を書くための書き方
配列の中身を1つずつ処理するには、For文またはFor Each文を使います。ここでもUBound・LBoundが活躍します。
5-1. For文での列挙(1次元配列)
Sub LoopFor1D()
Dim Data(1 To 5) As String
Data(1) = "田中": Data(2) = "佐藤": Data(3) = "鈴木"
Data(4) = "高橋": Data(5) = "伊藤"
Dim i As Long
For i = LBound(Data) To UBound(Data)
Debug.Print Data(i)
Next i
End Sub
ここでのポイントは、For i = 1 To 5のように数値を決め打ちせず、For i = LBound(Data) To UBound(Data)と書く点です。この書き方なら、配列の宣言範囲が変わってもコードを一切修正せずに対応できます。
「For i = 0 To 4」のように数値を直接書いたコードは、後から配列のサイズを変更したときにループ範囲がズレて、一部のデータが処理されなかったり、逆に存在しない要素にアクセスしてエラーになったりします。LBound・UBoundを使ったループは、この事故を根本から防ぐ書き方です。
5-2. For文での列挙(多次元配列)
Sub LoopFor2D()
Dim Data(1 To 3, 1 To 2) As String
Dim r As Long, c As Long
For r = LBound(Data, 1) To UBound(Data, 1)
For c = LBound(Data, 2) To UBound(Data, 2)
Debug.Print "(" & r & "," & c & ") = " & Data(r, c)
Next c
Next r
End Sub
📚 用語解説
For Each文:配列やコレクションの各要素を、インデックス番号を意識せず順番に取り出せる構文。「For i = LBound To UBound」より書き方がシンプルになる一方、要素の現在位置(何番目か)を取得できないという制約があります。
5-3. For Each文での列挙
Sub LoopForEach()
Dim Data(1 To 3) As String
Data(1) = "東京": Data(2) = "大阪": Data(3) = "福岡"
Dim v As Variant
For Each v In Data
Debug.Print v
Next v
End Sub
For Eachは記述がシンプルな反面、「今何番目の要素を処理しているか」が分からないというデメリットがあります。インデックス番号を使った処理(例: 隣の要素と比較する)が必要な場合はFor文、単純に全件処理したいだけならFor Eachという使い分けが基本です。
この2つの列挙処理の流れをまとめると、以下のように整理できます。
静的 or 動的
番号が必要か
単純処理か
番号操作が
必要な場合
全件処理
のみの場合
06 DYNAMIC ARRAY 動的配列(ReDim)と組み合わせた実務パターン 要素数が事前に分からないデータを扱う
実務では「何件データが来るか事前に分からない」というケースが多くあります。この場合に使うのが動的配列です。UBound・LBoundは、動的配列のサイズを確認する際にも欠かせません。
📚 用語解説
動的配列(ReDim):宣言時にサイズを確定させず、実行中にデータ量に応じてサイズを変更できる配列。Dim Data() As Stringのように空のカッコで宣言し、後からReDim Data(件数)でサイズを確定させます。件数が変動する請求書明細や顧客リストの処理でよく使われます。
6-1. ReDimで配列サイズを変更する
Sub DynamicArraySample()
Dim Data() As String
Dim lastRow As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
ReDim Data(1 To lastRow)
Dim i As Long
For i = 1 To lastRow
Data(i) = Cells(i, 1).Value
Next i
Debug.Print "取得件数: " & (UBound(Data) - LBound(Data) + 1)
End Sub
この例では、シートの最終行を取得してからReDimでその件数分の配列を確保しています。データ件数が毎回変わる帳票処理では、この「最終行取得 → ReDim → UBound/LBoundで検証」という流れが定番パターンです。
6-2. ReDim Preserveでデータを保持したまま拡張する
通常のReDimは配列の中身を初期化(消去)してしまいますが、ReDim Preserveを使うと既存データを保持したまま配列を拡張できます。
Sub RedimPreserveSample()
Dim Data() As String
Dim n As Long
n = 0
ReDim Data(1 To 1)
Data(1) = "1件目"
n = UBound(Data) + 1
ReDim Preserve Data(1 To n)
Data(n) = "2件目"
Debug.Print "現在の要素数: " & (UBound(Data) - LBound(Data) + 1)
End Sub
ReDim Preserveはループの中で毎回呼び出すと、実行速度が大幅に低下します。件数が多い処理(数百〜数千件)では、あらかじめ最大件数分の配列を確保しておき、最後にUBound・LBoundで実際の使用範囲だけ切り出す方が高速です。
実務でよくある活用例を挙げると、次のようなケースでUBound・LBoundと動的配列の組み合わせが役立ちます。
07 TROUBLESHOOTING よくあるエラーと対処法 「Subscript out of range」を中心に原因を整理する
UBound・LBoundを使ったコードで最もよく遭遇するのが、「実行時エラー9: Subscript out of range(インデックスが有効範囲にありません)」です。ここでは代表的な原因と対処法を整理します。
📚 用語解説
Subscript out of range(実行時エラー9):配列に存在しないインデックスにアクセスしようとしたときに発生するエラー。「配列の範囲外を指定した」ことを示すVBAの標準エラーメッセージで、UBound・LBoundを使ったコードで最も頻繁に遭遇するエラーの一つです。
7-1. 【原因1】未初期化の配列にUBoundを使う
動的配列を宣言しただけでReDimを実行する前にUBoundを呼び出すと、このエラーが発生します。
Sub ErrorSample1()
Dim Data() As String
Debug.Print UBound(Data) '実行時エラー9が発生
End Sub
対処法としては、UBoundを呼び出す前に配列が初期化済みかどうかをOn ErrorやIsArrayEmpty的な自作関数でチェックする方法が一般的です。
Function IsArrayInitialized(arr As Variant) As Boolean
On Error Resume Next
IsArrayInitialized = (UBound(arr) >= LBound(arr))
On Error GoTo 0
End Function
7-2. 【原因2】多次元配列で次元指定を誤る
先述のとおり、2次元配列に対してUBound(Data, 3)のように存在しない次元を指定すると、このエラーが発生します。次元数を意識せずにコピー&ペーストしたコードでよく起きるミスです。
7-3. 【原因3】Erase後にUBoundを呼び出す
動的配列に対してEraseを実行すると、配列は完全に未初期化の状態に戻ります。その直後にUBoundを呼び出すと、原因1と同じエラーになります。
静的配列(Dim Data(4)のように固定サイズで宣言したもの)にEraseを実行した場合は、要素の中身だけがクリアされ、配列自体は初期化されたままなのでUBoundは正常に動作します。一方、動的配列(Dim Data())の場合はEraseで配列自体が未初期化状態に戻るため、UBoundを呼ぶとエラーになります。この違いを知らずにハマる方が非常に多いです。
08 BEYOND VBA 【独自】VBAで消耗する前に知っておきたいAI自動化という選択肢 そもそも配列処理を自分で書かない、という発想
ここまでUBound・LBoundの使い方を細かく解説してきましたが、実務の現場では正直なところ「毎回このレベルの配列処理を手で書くのが負担」という声もよく聞きます。特に、非エンジニアの管理職が本業の合間にVBAを覚えて保守していくのは、決して楽な道ではありません。
弊社(株式会社GENAI)では、Claude CodeというAIエージェントを使って、こうした「Excelの集計・データ整形マクロ」の多くを、VBAを1行も書かずに自動化しています。
📚 用語解説
Claude Code:AnthropicがreleaseするAIエージェント。チャットで「この請求書データを集計して」と話しかけるだけで、必要なコード(VBA・Python問わず)を自ら書き、実行し、結果を確認するところまで自律的に行います。プログラミング未経験でも、日本語の指示だけで業務自動化を進められるのが特徴です。
8-1. VBAを自分で書く場合とAIに任せる場合の違い
| 項目 | VBAを自分で書く | Claude Codeに任せる |
|---|---|---|
| 習得コスト | UBound・LBound・ReDim等の文法を理解する必要あり | 日本語で依頼するだけ、文法知識は不要 |
| 保守 | 担当者が異動・退職すると誰も直せなくなりがち | AIに再度依頼すれば都度メンテナンス可能 |
| エラー対応 | Subscript out of range等を自力でデバッグ | エラー内容を伝えれば自動で修正案を提示 |
| 応用範囲 | Excel内で完結する処理が中心 | メール送信・他システム連携まで拡張しやすい |
弊社の実運用データで見ると、Claude Max 20xプラン(月額約30,000円)を契約し、経理・営業・広告・秘書業務など社内のあらゆる業務にClaude Codeを活用しています。特に経理領域では、請求書チェックや経費仕訳といったExcelでの集計・突合作業が、月40時間程度から月5時間程度まで圧縮できている肌感です。
ここまで解説してきたUBound・LBoundの考え方(配列の範囲を意識する、ループを安全に書く)は、AIにコードを書かせる際の指示出しにもそのまま活きます。「配列の要素数がずれていないか確認して」とAIに指示できるのは、この記事で学んだ知識があってこそです。VBAの基礎理解とAI活用は対立するものではなく、両輪だと捉えるのがおすすめです。
8-2. どちらを選ぶべきか の判断基準
「これから覚えるならVBAかAIか」と迷う方に向けて、シンプルな判断基準をお伝えします。
1回きりの処理
その場で完結
繰り返し発生
依頼して社内資産化
再利用可能な形で保存
単発の集計であればAIにその場で依頼して終わらせ、毎月繰り返す定型処理であれば、AIに生成させたマクロやスクリプトを社内の資産として残す——このハイブリッドな使い方が、非エンジニアの管理職にとって最も現実的な着地点だと考えています。
09 CONCLUSION まとめ UBound・LBoundを正しく使いこなすための要点整理
この記事では、VBA配列のUBound・LBound関数について、基本構文から多次元配列、要素数の正しい計算方法、For文・For Eachでの列挙、動的配列との組み合わせ、よくあるエラー対処、そしてAIによる自動化という選択肢までを解説しました。最後にポイントを振り返ります。
UBound・LBoundは、一見地味な関数ですが、配列を安全に扱うための土台になる知識です。今回のポイントを押さえておけば、要素数のズレや実行時エラーに悩まされることは大きく減るはずです。
一方で、「そもそも配列処理を毎回自分で書きたくない」という方には、AIエージェントに任せるという選択肢も現実的になってきています。VBAの基礎知識を土台にしつつ、繰り返し作業はAIに寄せていく——そんな働き方の参考に、この記事がお役に立てば幸いです。
ExcelマクロやVBAの手作業を、AI鬼管理が一緒に自動化します
毎月の集計・請求書チェック・顧客リスト整理——UBound・LBoundを駆使したVBAコードで消耗する前に、AIに任せられる範囲を一緒に見極めませんか。
弊社の実運用ノウハウをもとに、個別に自動化のご相談を承ります。
NEXT STEP
この記事の内容を、あなたのビジネスで
実践してみませんか?
よくある質問
Q. UBoundとLBoundは、配列の要素数そのものを返してくれますか?
A. いいえ、返しません。UBoundは上限インデックス、LBoundは下限インデックスを返すだけです。要素数を知りたい場合は「UBound - LBound + 1」という計算が必要です。下限が0か1かによってズレが生じるため、この計算式を必ず使うようにしてください。
Q. 多次元配列でUBoundの第2引数を省略するとどうなりますか?
A. 第2引数を省略すると、自動的に1次元目(多くの場合は行方向)の上限が返されます。2次元目・3次元目の値を取得したい場合は、必ず「UBound(Data, 2)」のように明示的に次元番号を指定する必要があります。
Q. 未初期化の配列にUBoundを使うとどうなりますか?
A. 「実行時エラー9: Subscript out of range」が発生します。動的配列(Dim Data() As String のように宣言したもの)は、ReDimでサイズを確定させるまでUBoundを呼び出せません。呼び出す前に配列が初期化済みかを確認する処理を挟むのが安全です。
Q. ReDim PreserveとReDimの違いは何ですか?
A. 通常のReDimは配列を再宣言する際に既存データを消去しますが、ReDim Preserveを使うと既存データを保持したまま配列サイズを変更できます。ただし、多次元配列でReDim Preserveを使う場合、最後の次元しかサイズ変更できないという制約があります。
Q. For文とFor Each文、配列処理ではどちらを使うべきですか?
A. 要素の位置(インデックス)を使った処理(隣接要素との比較や特定行の書き換えなど)が必要な場合はFor文、単純に全要素を順番に処理するだけならFor Each文が適しています。For Eachはコードがシンプルになる一方、位置情報を取得できない点に注意してください。
Q. Excelのセル範囲を配列に代入すると、下限は0と1のどちらになりますか?
A. Range("A1:D10").Valueのようにセル範囲を配列に代入した場合、下限は1になります。通常のDim宣言による配列(下限0がデフォルト)と挙動が異なるため、この種の配列を扱う際は必ずLBoundで下限を確認してから処理することをおすすめします。
Q. VBAの配列処理をAIに任せることは実際に可能ですか?
A. 可能です。Claude CodeのようなAIエージェントに「このExcelデータを集計して」と日本語で依頼するだけで、必要なコードを自動生成・実行してくれます。弊社でも経理・営業領域のExcel集計作業の多くをこの方法で自動化しており、月40時間かかっていた作業を月5時間程度まで圧縮できている実感があります。
Claude Codeで業務自動化を90日で叩き込む
経営者向けの伴走型パーソナルトレーニング
AI鬼管理/AIBPO by AI鬼管理へのお問い合わせ
この記事を読んで気になった方へ。
専門スタッフが、御社に最適な
業務自動化・業務代行プランを無料でご提案します。




