2012-01-13 38 views
35

Mã sau đây cho tôi lỗi 9 "chỉ số ngoài phạm vi". Tôi có nghĩa là khai báo một mảng động để kích thước thay đổi khi tôi thêm các phần tử vào nó. Tôi có phải tạo ra một "điểm" trên mảng trước khi tôi lưu trữ một cái gì đó trong nó như trong JS?Nhập các mảng động 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 

Trả lời

47

tại của bạn cho vòng lặp sử dụng một Redim trên mảng như ở đây:

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

Tại sao bạn làm điều đó * trong * vòng lặp? 'ReDim', và đặc biệt là khi bạn thêm' Preserve', là một kẻ giết người hiệu suất tiềm năng. Bạn biết bao nhiêu lần vòng lặp sẽ lặp đi lặp lại, do đó chắc chắn làm điều đó bên ngoài vòng lặp. Sau đó, bạn chỉ thay đổi kích thước mảng một lần, và bạn không cần 'Preserve'. –

+9

@CodyGray Bạn hoàn toàn đúng nếu kích thước mảng cuối cùng đã được xác định khi nhập vòng lặp. Đặt Redim bên trong vòng lặp sẽ là một kẻ giết người hiệu suất. Tuy nhiên, tôi giả định rằng kích thước của mảng không được xác định khi nhập vòng lặp. Nếu không, toàn bộ mẫu không có ý nghĩa gì cả ... – Fluffi1974

+3

Nó * có * được xác định khi nhập vòng lặp. Bạn phải xác định phạm vi của vòng lặp. Thậm chí nếu nó là một biến thay vì một hằng số như '3', bạn vẫn đang xác định một giới hạn trên trong câu lệnh' For'. Sử dụng nó để tự động khởi tạo kích thước của mảng. –

21

Vâng, bạn đang tìm kiếm tuyên bố ReDim, mà tự động phân bổ số tiền yêu cầu của không gian trong mảng.

Tuyên bố sau

Dim MyArray() 

tuyên bố một mảng mà không cần kích thước, vì vậy trình biên dịch không biết lớn như thế nào nó là gì và không thể lưu trữ bất cứ điều gì bên trong của nó.

Nhưng bạn có thể sử dụng câu lệnh ReDim để thay đổi kích thước các mảng:

ReDim MyArray(0 To 3) 

Và nếu bạn cần thay đổi kích thước mảng trong khi giữ gìn nội dung của nó, bạn có thể sử dụng từ khóa Preserve cùng với báo cáo kết quả ReDim:

ReDim Preserve MyArray(0 To 3) 

Nhưng đừng lưu ý rằng cả ReDim và đặc biệt là ReDim Preserve có chi phí hoạt động nặng. Cố gắng tránh làm điều này hơn và hơn trong một vòng lặp nếu có thể; người dùng của bạn sẽ cảm ơn bạn.


Tuy nhiên, trong ví dụ đơn giản thể hiện trong câu hỏi của bạn (nếu nó không chỉ là một mẫu throwaway), bạn không cần phải ReDim ở tất cả. Chỉ cần khai báo mảng có kích thước rõ ràng:

Dim MyArray(0 To 3) 
+3

+1 mặc dù tôi sẽ thêm rằng giới hạn dưới có thể và theo ý kiến ​​của tôi nên được quy định rõ ràng: 'ReDim MyArray (0 đến 3)' –

+1

@Jean: Đó là lời khuyên tốt. Rất nhiều người bị cắn bởi thực tế là VB (A) hỗ trợ các giới hạn thấp hơn 0. Rõ ràng là luôn luôn thực hành tốt. –

9

Ngoài các nhận xét hữu ích của Cody, điều đáng chú ý là đôi khi bạn sẽ không biết mảng của mình lớn như thế nào. Hai tùy chọn trong tình huống này là

  1. Tạo một mảng lớn, đủ để xử lý bất cứ điều gì bạn nghĩ sẽ được ném vào nó
  2. Sensible sử dụng Redim Preserve

Đoạn code dưới đây cung cấp một ví dụ về một thói quen sẽ kích thước myArray phù hợp với biến số lngSize, sau đó thêm các phần tử bổ sung (bằng kích thước mảng ban đầu) bằng cách sử dụng kiểm tra Mod bất cứ khi nào giới hạn trên vượt quá

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

Áp phích đầu tiên, trình đọc thời gian dài. Như Cody và Brett đã đề cập, bạn có thể giảm sự chậm trễ của VBA với việc sử dụng hợp lý Redim Preserve. Brett đã đề xuất Mod để thực hiện việc này.

Bạn cũng có thể sử dụng người dùng được xác định TypeSub để thực hiện việc này.Hãy xem xét mã của tôi bên dưới:

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 

Chỉ gọi khi kích thước đã tăng gấp đôi. Biến thành viên eSize theo dõi kích thước dữ liệu thực tế của eElems. Cách tiếp cận này đã giúp tôi cải thiện hiệu suất khi độ dài mảng cuối cùng không được biết đến cho đến khi thời gian chạy.

Hy vọng điều này cũng giúp người khác.

+1

bạn nhận ra câu hỏi này đã được hỏi (và trả lời) 18 tháng trước, phải không? –

+15

có! cũng muốn cung cấp một giải pháp thay thế cho các độc giả khác. tôi nhận ra op đã chấp nhận câu trả lời trước đó rồi. cảm ơn. – a505999

+0

Vậy thì đây là +1 cho bạn, thay thế thú vị! :) –

8

Tôi thấy nhiều (tất cả) bài viết trên dựa vào LBound/UBound cuộc gọi khi chưa có khả năng chưa được khởi tạo mảng động VBA, những gì gây ra cái chết không thể tránh khỏi ứng dụng của ...

đang thất thường:

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

Mã chính xác:

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

... tức là bất kỳ mã nào trong đó Dim arr1() được theo dõi ngay sau đó bởi LBound(arr1)/UBound(arr1) cuộc gọi mà không có ReDim arr1(...) ở giữa, bị treo. Vòng xoay là sử dụng số On Error Resume Next và kiểm tra số Err.Number ngay sau cuộc gọi LBound(arr1)/UBound(arr1) - số này phải là 0 nếu mảng được khởi tạo, nếu không khác. Vì có một số sai sót tích hợp của VBA, việc kiểm tra thêm các giới hạn của mảng là cần thiết. giải thích chi tiết có thể tất cả mọi người đọc ở Chip Pearson's website (mà nên được tổ chức như một Mankind Treasure Of Wisdom VBA ...)

Heh, đó là bài viết đầu tiên của tôi, tin rằng nó là rõ ràng.

+0

Nhận xét đó tất cả được mã hóa ở trên dựa trên ubound/lbound là không chính xác – brettdj

Các vấn đề liên quan