填充VBA动态数组

seb*_*anc 46 arrays vba

以下代码给出了错误9"下标超出范围".我的意思是声明一个动态数组,以便在我向其添加元素时更改维度.在我像JS一样存储内容之前,我是否必须在数组上创建一个"点"?

Sub test_array()
    Dim test() As Integer
    Dim i As Integer
    For i = 0 To 3
        test(i) = 3 + i
    Next i
End Sub
Run Code Online (Sandbox Code Playgroud)

Flu*_*974 59

在你的for循环中使用数组上的Redim,如下所示:

For i = 0 to 3
  ReDim Preserve test(i)
  test(i) = 3 + i
Next i
Run Code Online (Sandbox Code Playgroud)

  • 你为什么要在循环中做*`ReDim`,特别是当你添加`Preserve`时,它是一个潜在的性能杀手.你知道循环要迭代多少次,所以一定要在循环之外进行.那么你只需要调整一次数组的大小,而你不需要`Preserve`. (18认同)
  • @CodyGray如果在进入循环时已经定义了最终的数组大小,那么你是完全正确的.将Redim置于循环内部将成为性能杀手.但是,我假设在进入循环时没有确定数组的大小.否则整个样本根本就没有意义...... (14认同)
  • 也许这个解决方案在优化方面不是很好,但是如果你不知道前面的大小 - 例如在一些条件下做一个while循环 - 那么这可能是最简单的解决方案 (5认同)
  • 它*在进入循环时定义*.您必须定义循环的范围.即使它是一个变量而不是像`3`这样的常量,你仍然在`For`语句中指定一个上限.用它来动态初始化数组的大小. (3认同)
  • 你可以使用"Exit For"离开数组 (3认同)

Cod*_*ray 24

是的,您正在寻找ReDim语句,该语句动态分配数组中所需的空间量.

以下声明

Dim MyArray()
Run Code Online (Sandbox Code Playgroud)

声明一个没有维度的数组,因此编译器不知道它有多大并且不能在其中存储任何内容.

但是您可以使用该ReDim语句来调整数组的大小:

ReDim MyArray(0 To 3)
Run Code Online (Sandbox Code Playgroud)

如果您需要在保留其内容的同时调整数组大小,可以将该Preserve关键字与ReDim语句一起使用:

ReDim Preserve MyArray(0 To 3)
Run Code Online (Sandbox Code Playgroud)

但要注意两者ReDim并且特别ReDim Preserve是性能成本很高.尽量避免在循环中反复这样做; 你的用户会感谢你.


但是,在您的问题中显示的简单示例中(如果它不仅仅是一次性样本),您根本不需要ReDim.只需声明具有显式尺寸的数组:

Dim MyArray(0 To 3)
Run Code Online (Sandbox Code Playgroud)

  • +虽然我会补充说下限可以并且在我看来应该明确指定:`ReDim MyArray(0 To 3)` (3认同)

a50*_*999 24

正如科迪和布雷特所提到的那样,你可以通过合理使用来减少VBA的减速Redim Preserve.布雷特建议Mod这样做.

您还可以使用定义的用户TypeSub执行此操作.考虑下面的代码:

Public Type dsIntArrayType
   eElems() As Integer
   eSize As Integer
End Type

Public Sub PushBackIntArray( _
    ByRef dsIntArray As dsIntArrayType, _
    ByVal intValue As Integer)

    With dsIntArray
    If UBound(.eElems) < (.eSize + 1) Then
        ReDim Preserve .eElems(.eSize * 2 + 1)
    End If
    .eSize = .eSize + 1
    .eElems(.eSize) = intValue
    End With

End Sub
Run Code Online (Sandbox Code Playgroud)

ReDim Preserve仅在大小加倍时调用此方法.成员变量eSize跟踪实际数据大小eElems.这种方法帮助我提高性能,直到运行时才知道最终的数组长度.

希望这也有助于其他人.

  • 是!我也希望为其他读者提供替代方案.我意识到操作已经接受了之前的答案.谢谢. (17认同)
  • +1.这类似于在C++标准库中实现动态数组的方式.为了封装的目的,我会将push_back设为一个方法,并将类型设为类. (5认同)

bre*_*tdj 11

除了Cody的有用评论之外,值得注意的是,有时您不会知道阵列应该有多大.这种情况下的两个选择是

  1. 创建一个足够大的数组来处理你认为会被抛出的任何东西
  2. 明智地使用Redim Preserve

下面的代码提供了一个例程,它将myArray根据lngSize变量进行尺寸标注,然后Mod在即将超过上限时通过使用测试添加其他元素(等于初始数组大小)

Option Base 1

Sub ArraySample()
    Dim myArray() As String
    Dim lngCnt As Long
    Dim lngSize As Long

    lngSize = 10
    ReDim myArray(1 To lngSize)

    For lngCnt = 1 To lngSize*5
        If lngCnt Mod lngSize = 0 Then ReDim Preserve myArray(1 To UBound(myArray) + lngSize)
        myArray(lngCnt) = "I am record number " & lngCnt
    Next
End Sub
Run Code Online (Sandbox Code Playgroud)


Pet*_*nka 10

我看到上面有很多(所有)帖子依赖LBound/ UBound调用可能未初始化的VBA动态数组,导致应用程序无法死亡的原因......

不稳定的代码:

Dim x As Long Dim arr1() As SomeType ... x = UBound(arr1) 'crashes

正确的代码:

Dim x As Long Dim arr1() As SomeType ... ReDim Preserve arr1(0 To 0) ... x = UBound(arr1)

...即没有在/ 之间Dim arr1()立即跟随LBound(arr1)/ UBound(arr1)呼叫的任何代码ReDim arr1(...),崩溃.环形交叉口是在/ call 之后使用On Error Resume Next并检查Err.Number右边- 如果数组初始化则应为0,否则为非零.由于存在一些VBA内置的不当行为,因此需要进一步检查数组的限制.每个人都可以在Chip Pearson的网站上阅读详细解释(应该将其视为VBA智慧人类宝藏 ...)LBound(arr1)UBound(arr1)

嘿,这是我的第一篇文章,相信它是清晰易读的.