文章目录

VBA 数组操作:ReDim、UBound、LBound

发布于 2026-04-05 12:05:42 · 浏览 21 次 · 评论 0 条

VBA 数组操作:ReDim、UBound、LBound

在 VBA 开发中,数组是最常用的数据结构之一。但很多初学者对数组的大小控制、边界获取感到困惑。本文将详细介绍三个核心操作:如何声明和调整数组大小的 ReDim,以及如何获取数组边界的 UBoundLBound。通过这些知识,你将能够灵活处理各种数据集合。


一、数组基础与动态数组的概念

在 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。LBoundUBound 会根据这一设置返回正确的边界值:

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 多维数组的边界获取

对于多维数组,LBoundUBound 可以指定要查询的维度。第二个参数表示维度编号:

' 二维数组
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

这个示例展示了两个重要技巧:首先,始终使用 LBoundUBound 遍历数组,而非固定数值;其次,通过错误处理检测数组是否已分配空间。


四、综合应用示例

下面通过一个完整的示例,将 ReDimUBoundLBound 结合起来解决实际问题:从工作表中读取数据,动态存储后进行统计。

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) 分别获取行数和列数
  • 根据实际维度动态分配结果数组
  • 使用 LBoundUBound 安全遍历结果数组
  • 正确处理二维数组的索引(默认从 1 开始)

五、最佳实践总结

操作 说明 注意事项
ReDim 首次为动态数组分配大小 会清除所有现有数据
ReDim Preserve 扩展数组并保留数据 只能改变最后一维的大小
LBound(arr) 返回数组下界 多维数组可用第二参数指定维度
UBound(arr) 返回数组上界 多维数组可用第二参数指定维度

掌握这三个操作后,你就能够灵活处理各种规模的数组数据。记住以下原则:处理动态数组前先用 LBoundUBound 获取边界,扩展数组时根据是否需要保留数据选择是否使用 Preserve

评论 (0)

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

扫一扫,手机查看

扫描上方二维码,在手机上查看本文