VBA 数组操作:ReDim、UBound、LBound
在 VBA 开发中,数组是最常用的数据结构之一。但很多初学者对数组的大小控制、边界获取感到困惑。本文将详细介绍三个核心操作:如何声明和调整数组大小的 ReDim,以及如何获取数组边界的 UBound 和 LBound。通过这些知识,你将能够灵活处理各种数据集合。
一、数组基础与动态数组的概念
在 VBA 中,数组是一组相同类型数据的集合。声明数组时,需要指定其大小,这决定了数组能容纳多少个元素。
' 声明一个包含 5 个元素的数组,索引从 0 到 4
Dim scores(4) As Integer
上述代码创建了一个固定大小的数组。一旦声明,数组的长度就无法改变。但在实际开发中,我们经常需要在运行时根据数据量动态调整数组大小。这时就需要使用动态数组。
动态数组在声明时不指定具体大小,保留 Empty 括号:
' 声明动态数组
Dim data() As String
声明后,数组是空的,长度为 0。此时不能直接存储数据,需要使用 ReDim 语句来分配实际大小。
二、ReDim:调整数组大小
ReDim 语句用于为动态数组重新分配存储空间。这是动态数组的核心操作。
2.1 基本用法
首次使用 ReDim 分配空间:
Dim names() As String
' 第一次分配大小
ReDim names(2) ' 创建包含 3 个元素的数组,索引 0-2
names(0) = "张三"
names(1) = "李四"
names(2) = "王五"
执行 ReDim names(2) 后,names 数组包含 3 个元素。需要注意的是,每次使用 ReDim 都会重置数组中的所有数据。如果你想保留原有数据,需要使用 Preserve 关键字。
2.2 使用 Preserve 保留数据
在调整数组大小时,如果希望在保留原有数据的基础上扩展数组,使用 Preserve 关键字:
Dim items() As String
' 初始分配
ReDim items(1) As String
items(0) = "苹果"
items(1) = "香蕉"
' 扩展数组,保留原有数据
ReDim Preserve items(3) ' 现在有 4 个元素
items(2) = "橙子"
items(3) = "葡萄"
执行完毕后,数组包含 "苹果"、"香蕉"、"橙子"、"葡萄" 四个元素,原有数据全部保留。
重要限制:使用 Preserve 时,只能改变数组最后一维的大小。对于一维数组,这没有问题;但对于多维数组,只能调整最后一维:
' 多维数组示例
Dim matrix() As Integer
' 声明二维动态数组
ReDim matrix(1, 1) ' 2x2 数组
' 只能扩展最后一维(列),不能扩展第一维(行)
ReDim Preserve matrix(1, 3) ' 合法:变为 2x4 数组
' ReDim Preserve matrix(3, 1) ' 错误:不能改变第一维
2.3 ReDim 的常见场景
ReDim 通常与循环结合使用,在未知数据量的情况下逐个添加元素:
Dim result() As Integer
Dim count As Integer
Dim inputValue As Integer
count = 0
' 假设从用户获取一系列正整数,遇到负数结束
inputValue = 5
Do While inputValue > 0
ReDim Preserve result(count) ' 扩展数组
result(count) = inputValue
count = count + 1
' 模拟输入,实际中可能是 InputBox 或读取单元格
inputValue = 10 ' 简化示例
Loop
这段代码展示了典型的动态扩容模式:每次循环扩展数组一个元素,并使用 Preserve 保留已有数据。
三、UBound 与 LBound:获取数组边界
动态数组的大小可以随时改变,但在操作数组元素之前,必须知道数组的当前边界。UBound 返回数组的上界(最大索引),LBound 返回数组的下界(最小索引)。
3.1 基本语法与返回值
VBA 中数组的默认下界是 0,但可以通过 Option Base 1 改为 1。LBound 和 UBound 会根据这一设置返回正确的边界值:
Dim arr(5) As Integer ' 固定大小数组
' 获取边界
Dim lower As Integer, upper As Integer
lower = LBound(arr) ' 返回 0
upper = UBound(arr) ' 返回 5
对于动态数组,在分配大小后同样可以使用这两个函数:
Dim dynamicArr() As String
ReDim dynamicArr(10)
MsgBox "下界: " & LBound(dynamicArr) & ", 上界: " & UBound(dynamicArr)
' 输出:下界: 0, 上界: 10
3.2 多维数组的边界获取
对于多维数组,LBound 和 UBound 可以指定要查询的维度。第二个参数表示维度编号:
' 二维数组
Dim grid(3, 5) As Integer
' 获取第一维(行)的边界
Dim rowLower As Integer, rowUpper As Integer
rowLower = LBound(grid, 1) ' 返回 0
rowUpper = UBound(grid, 1) ' 返回 3
' 获取第二维(列)的边界
Dim colLower As Integer, colUpper As Integer
colLower = LBound(grid, 2) ' 返回 0
colUpper = UBound(grid, 2) ' 返回 5
这个特性在遍历多维数组时特别有用,可以针对每个维度使用正确的循环范围。
3.3 实际应用:安全遍历数组
在操作数组之前,先获取边界可以确保代码的健壮性,特别是对于动态数组:
Sub ProcessData()
Dim data() As Variant
Dim i As Integer
' 确保数组已分配大小
If Not IsArrayAllocated(data) Then
ReDim data(0 To 9)
End If
' 安全遍历:使用 UBound/LBound
For i = LBound(data) To UBound(data)
' 处理每个元素
Debug.Print "元素 " & i & ": " & data(i)
Next i
End Sub
Function IsArrayAllocated(arr() As Variant) As Boolean
On Error Resume Next
IsArrayAllocated = (UBound(arr) >= 0)
On Error GoTo 0
End Function
这个示例展示了两个重要技巧:首先,始终使用 LBound 和 UBound 遍历数组,而非固定数值;其次,通过错误处理检测数组是否已分配空间。
四、综合应用示例
下面通过一个完整的示例,将 ReDim、UBound、LBound 结合起来解决实际问题:从工作表中读取数据,动态存储后进行统计。
Sub ReadAndStatistics()
Dim rawData() As Variant
Dim result() As Double
Dim i As Long, j As Long
Dim rowCount As Long, colCount As Long
' 读取工作表数据到数组(假设数据在 Sheet1)
rawData = Worksheets("Sheet1").Range("A1").CurrentRegion.Value
' 获取数据维度
rowCount = UBound(rawData, 1) ' 行数
colCount = UBound(rawData, 2) ' 列数
' 动态分配结果数组(用于存储每行的平均值)
ReDim result(0 To rowCount - 1)
' 计算每行平均值
For i = 1 To rowCount ' 注意:二维数组下界通常是 1
Dim rowSum As Double
rowSum = 0
For j = 1 To colCount
rowSum = rowSum + rawData(i, j)
Next j
result(i - 1) = rowSum / colCount
Next i
' 输出结果
For i = LBound(result) To UBound(result)
Debug.Print "第 " & (i + 1) & " 行的平均值: " & result(i)
Next i
End Sub
这个示例涵盖了以下关键点:
- 使用
UBound(..., 1)和UBound(..., 2)分别获取行数和列数 - 根据实际维度动态分配结果数组
- 使用
LBound和UBound安全遍历结果数组 - 正确处理二维数组的索引(默认从 1 开始)
五、最佳实践总结
| 操作 | 说明 | 注意事项 |
|---|---|---|
ReDim |
首次为动态数组分配大小 | 会清除所有现有数据 |
ReDim Preserve |
扩展数组并保留数据 | 只能改变最后一维的大小 |
LBound(arr) |
返回数组下界 | 多维数组可用第二参数指定维度 |
UBound(arr) |
返回数组上界 | 多维数组可用第二参数指定维度 |
掌握这三个操作后,你就能够灵活处理各种规模的数组数据。记住以下原则:处理动态数组前先用 LBound 和 UBound 获取边界,扩展数组时根据是否需要保留数据选择是否使用 Preserve。

暂无评论,快来抢沙发吧!