1. はじめに
Excel VBAは導入しやすく、実務でも使用しやすい言語です。
そして比較的ゆるい言語でもあります。
その「ゆるさ」は、初心者にとっては優しさであり、
一方で実務では思わぬアクシデントを引き起こす原因にもなります。
本記事では、ある程度VBAを理解した人が使い始める配列変数で混乱の元となる
Excel VBAの独特な「配列要素」の表現について扱います。
2. 配列の0数え/1数えの問題
Excel VBAでは、配列を自分で宣言すると0数えになるのが一般的です。
一方で、
セル範囲をそのまま配列として受け取ると1数えの2次元配列になります。
このクセを意識していないと要素の見落としや思わぬエラーが発生します。
3. 配列変数を宣言した場合(0始まり)
まずは、普通の配列宣言です。
Sub Sample()
Dim arr(3) As String
arr(0) = "A"
arr(1) = "B"
arr(2) = "C"
arr(3) = "D"
End Sub
この場合、
- インデックス:0 =
A - インデックス:1 =
B - インデックス:2 =
C - インデックス:3 =
D
要素数:4
という、0数えの配列になります。
多くの言語では、配列はこちらの0数えが一般的です。
4. セル範囲を直接配列に代入した場合(1数え)
次に、セル範囲を直接配列として取得します。
Sub Sample()
Dim arr As Variant
arr = Range("A1:A3").Value
End Sub
このときの配列は、
- インデックス:(1 ,1)=
セルA1の値 - インデックス:(2 ,1)=
セルA2の値 - インデックス:(3 ,1)=
セルA3の値
要素数:3
という 1数えの2次元配列になります。
インデックス番号のみを見た場合、3.も 4.も上限は3ですが、要素数は異なります。
これが混乱の元です。
5. なぜこんな違いが起きるのか
理由は単純で、配列を作っている主体が違うからです。
-
Dim arr()
→ VBAが作る配列(言語仕様) -
Range.Value
→ Excelが返す配列(Excel仕様)
Excelはセル番号を、
行:1から
列:1から
数えています。
そのため、セル範囲を配列として返すとExcel都合の1始まり配列になるのです。
6. よくある事故例
次のようなコードは、非常によく見かけます。
Sub Sample()
Dim arr As Variant
arr = Range("A1:A3").Value
Dim i As Long
For i = 0 To 2
Debug.Print arr(i)
Next i
End Sub
このコードは、
エラーになることもある
環境や書き方次第では動く
しかし正しくないという 危険な状態です。
原因は、配列の開始位置を思い込みで決めていることです。
7. 対策① LBound / UBound を使う
この問題への最も安全な対策は、LBound と UBound を必ず使うことです。
- LBound = 配列の下限(始まり)を取得する関数
- UBound = 配列の上限(終わり)を取得する関数
LBound(arr, 1)
第一引数に配列変数、第二引数(省略可能)に指定したい次元番号を入力します。
(次元番号については下記参照)
2次元配列の場合は必ず第二引数も記述します。
- 第二引数(次元番号)指定の例
Sub Sample()
Dim arr As Variant
arr = Range("A1:B3").Value
Debug.Print UBound(arr,1)
Debug.Print UBound(arr,2)
End Sub
結果:
-
Debug.Print1行目は1次元目(A列目~B列目)を指定したので2 -
Debug.Print2行目は2次元目(1行目~3行目)を指定したので3
8. LBound / UBound を使った正しい書き方
- 1次元配列の場合
Sub Sample()
Dim arr(5) as String
Dim i As Long
For i = LBound(arr) To UBound(arr)
Debug.Print arr(i)
Next i
End Sub
これで、0始まり、1始まり、宣言方法の違いをすべて吸収できます。
- 2次元配列(Range.Value)の場合
Sub Sample()
Dim arr as Variant
arr = Range("A1:A3").Value
Dim r As Long
For r = LBound(arr, 1) To UBound(arr, 1)
Debug.Print arr(r, 1)
Next r
End Sub
必ず第二引数次元を記述します。
9. なぜこの問題が厄介なのか
この問題が厄介なのは、
- エラーにならないことが多い
- データが少ないと気づかない
- 処理結果が少しだけズレる
という点です。
ズレたまま処理が進み、後工程で初めて発覚する非常にコストの高い不具合です。
10. まとめ
-
VBA配列は常に0始まりとは限らない
-
Rangeから取得した配列は1始まり
-
配列の開始位置を決めつけずLBound / UBound を使う
結論として、
配列は「誰が作ったか」を意識する
これを守るだけで、配列まわりの事故は大幅に減ります。