2012-01-13 191 views
35

下面的代碼給我錯誤9「下標超出範圍」。我的意思是聲明一個動態數組,以便在添加元素時維度發生變化。在我像JS一樣存儲內容之前,是否必須在數組上創建一個「點」?填充VBA動態數組

Sub test_array() 
    Dim test() As Integer 
    Dim i As Integer 
    For i = 0 To 3 
     test(i) = 3 + i 
    Next i 
End Sub 

回答

47

在for循環使用REDIM陣列上喜歡這裏:

For i = 0 to 3 
    ReDim Preserve test(i) 
    test(i) = 3 + i 
Next i 
+17

你爲什麼會做* *中環? 'ReDim',特別是當你添加'Preserve'時,是一個潛在的性能殺手。你知道循環要迭代多少次,所以一定要在循環之外做。然後你只調整一次數組的大小,並且你不需要'Preserve'。 – 2012-01-13 13:10:04

+9

@CodyGray如果在進入循環時已經定義了最終的數組大小,那麼你是絕對正確的。將Redim放在循環中會是一個性能殺手。但是,我認爲數組的大小在進入循環時沒有確定。否則,整個樣本根本沒有意義... – Fluffi1974 2012-01-13 13:29:58

+3

它*具有*在進入循環時被定義。你必須定義循環的範圍。即使它是一個變量而不是像'3'這樣的常量,您仍然在'For'語句中指定了一個上限。使用它來動態初始化數組的大小。 – 2012-01-13 13:30:57

21

是的,你要尋找的ReDim語句,動態地分配在陣列中所需的空間量。

下面的語句

Dim MyArray() 

聲明沒有維數組,所以編譯器不知道它是多麼大,不能存儲它裏面什麼。

但是你可以使用ReDim語句來調整數組:

ReDim MyArray(0 To 3) 

如果你需要調整的陣列,同時在保持它的內容,你可以用ReDim語句一起使用Preserve關鍵字:

ReDim Preserve MyArray(0 To 3) 

但是千萬注意,這兩個ReDim特別ReDim Preserve有一個沉重的性能代價。如果可能的話,儘量避免一遍又一遍地循環播放;你的用戶會感謝你。


然而,在你的問題中所示(如果它不只是一個一次性的樣品)的簡單的例子,你不需要ReDim可言。只要有明確的尺寸聲明數組:

Dim MyArray(0 To 3) 
+3

+1雖然我想補充的是,下界可以,在我看來應該是被明確指定:'使用ReDim MYARRAY(0-3)' – 2012-01-13 13:22:15

+1

@Jean:這是很好的建議。很多人都被VB(A)支持0以外的下界這一事實所困擾。明確表示總是很好的做法。 – 2012-01-13 13:24:01

9

除了科迪的有益的意見值得注意的是,有時你不會知道你的陣列應該多大。在這種情況下,這兩個選項是

  1. 創建足夠大,以處理任何你想的陣列將它
  2. 明智使用Redim Preserve

下面的代碼提供了一個例子被拋出將在線路維度myArraylngSize可變的例程,然後通過使用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 
20

第一次海報,長時間讀者。作爲科迪和Brett提到的,你可以減少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 

這隻有當尺寸增加一倍時纔會撥打ReDim Preserve。成員變量eSize跟蹤eElems的實際數據大小。直到運行時才知道最終數組長度,這種方法幫助我提高了性能。

希望這會幫助別人了。

+1

你意識到這個問題在18個月前被問及(並回答)了吧? – 2013-08-21 19:07:25

+15

是的!也想爲其他讀者提供替代方案。我意識到這個操作已經接受了以前的答案。謝謝。 – a505999 2013-08-21 19:08:39

+0

那麼這裏有一個爲你+1,有趣的替代! :) – 2013-08-21 19:13:15

8

我見上面後但可能未初始化VBA動態數組依靠LBound/UBound電話很多(都有)職位,是什麼原因導致應用程序的必然死亡......

不穩定代碼:

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(...)之間,崩潰immediatelly隨後的任何代碼。迴旋是採用一個On Error Resume NextLBound(arr1)/UBound(arr1)呼叫後檢查Err.Number右 - 它應該是0,如果該陣列被初始化,否則非零。由於存在一些VBA內置錯誤行爲,因此需要進一步檢查陣列的限制。詳細的解釋可能大家閱讀Chip Pearson's website(應當慶賀作爲人類寶VBA智慧 ...)

嘿,那是我的第一篇文章,認爲這是清晰可辨。

+0

該評論重新編碼上面依賴ubound/lbound是不正確的 – brettdj 2015-04-27 11:08:07