Zählermarke der VG Wort Übersicht · Array()-Funktion · Arrays im VBA-Code · Arrays in Excel · Allgemeines

Arrays dimensionieren

Die Anzahl der Elemente und die Dimensionen eines Arrays kann man auf verschiedene Art festlegen:


Dim A(5) As Double

Eindimensionales Array. Ohne Option Base oder mit Option Base 0 sind A(0) bis A(5) adressierbar. Mit Option Base 1 sind A(1) bis A(5) adressierbar. Bei numerischen Datentypen wird jedes Element mit 0 initialisiert.

Dim X(-2 to 5) As Integer

Eindimensionales Array, die Indizes werden von -2 bis +5 gezählt, es können also 8 Elemente adressiert werden. Die Dimension ist zur Laufzeit nicht mehr veränderbar, und Option Base hat keinen Einfluss.

Dim M(1 To 7, 1 To 5) As Double

Zweidimensionales Array. Vom Datentyp und von den Indizes passt dies zu einer 7×5 Matrix. Die Dimension ist zur Laufzeit nicht mehr veränderbar, und Option Base hat keinen Einfluss.

Dim TempTextA(5) As String

Speichert 5 Werte als Text, je nach Option Base Einstellung von 0…5 oder von 1...5; Dimension zur Laufzeit nicht mehr veränderbar. Jedes Element des Arrays wird mit vbNullString ("") initialisiert, für die Strings besteht keine Längenbegrenzung.

Dim TempTextB(5) As String * 3

Wie oben, allerdings hat der Text immer eine feste Länge von 3 Zeichen und jedes Element wird mit vbNullChar (00h, hier wegen der Länge 3 mit 000000h) initialisiert. Der Vergleich TempTextB(2) = vbNullString oder wird also nie True ergeben, da jedes Element immer genau 3 Zeichen hat.

Dim WochenTag(1 To 366) As vbDayOfWeek

In VBA gibt es zahlreiche Konstanten, wie z. B. die Wochentage. Natürlich kann man diese als Typ heranziehen. Der Vorteil liegt darin, dass nun IntelliSense Vorschläge macht, wenn man den Wert in ein Element des Arrays eintragen will. In diesem Falle ist das kompatibel, aber unökonomisch, denn vbDayOfWeek ist vom Typ Long (4 Bytes × 366 Elemente), aber für Wochentage reicht der Typ Byte (1 Byte × 366 Elemente) aus.

Dim Werte() As Long
ReDim Werte(1 to 3, 1 to 3)

Hier wird zunächst die Variable Werte() als Array vom Typ Long deklariert und später mit ReDim auf die richtige Größe gebracht. In diesem Beispiel habe ich Zahlen für die Dimensionen eingetragen, aber bei ReDim können statt dessen auch Variable eingesetzt werden. Mit ReDim Preserve lassen sich später Arrays verkleinern oder vergrößern, ohne dass die verbliebenen Felder ihre Daten verlieren. Vorsicht: Bevor erstmals ReDim eingesetzt wurde, liefern LBound() und UBound() einen Fehler. Mit IsArray() lässt sich hier aber schon feststellen, dass es sich bei Werte um ein Datenfeld handelt.

Dim Werte
ReDim Werte(1 to 3, 1 to 3) As Long

Hier wurde Werte als Variant definiert (fehlende Typdefinition erzeugt immer Variant), deshalb kann man den Datentyp Long auch später festlegen. Nur hat man hier eben kein echtes Datenfeld, sondern eine einzelne Variant-Variable, die mit einem Datenfeld gefüllt wird.

Besonderheiten von ReDim:

Quiz für Codeversteher:

Der Geltungsbereich einer Array-Variablen hängt vom Ort ab, an dem es mit Dim deklariert wird:

Messen von Arraygrößen

Wenn ein Array ohne Dimensionen angelegt wurde, enthält es noch keine Elemente. Um den Beladungszustand eines Arrays festzustellen kann man beispielsweise so vorgehen:


Public Sub Größe()
    Dim Werte() As Long
    MsgBox IsArray(Werte) ' Ist es ein Array?
    MsgBox LBound(Werte) ' Untere Grenze?
    MsgBox UBound(Werte) ' Obere Grenze?
End Sub

IsArray() erkennt zuverlässig auch Arrays in Variant-Werten. Aber das Problem ist, dass in diesem halb-definierten Zustand der oben angelegten Variablen Werte() die Funktionen UBound() und LBound() zu einem Laufzeitfehler führen - egal welchen Datentyp Werte hat (auch Variant!).

Dieses Verhalten ist höchst inkonsistent, denn ParamArray gibt bei einem leeren Array LBound(Werte) = 0 und UBound(Werte) = -1 und ebenfalls problemlos ist es, wenn man einem leer dimensionierten Variant-Array mit … = Array() ein leeres Array zuweist.

VBA fehlt auch eine Funktion, um die Anzahl der Dimensionen zu bestimmen. Hier ist also eine etwas unschöne Funktion erforderlich, die einen Laufzeitfehler zur Erkennung braucht, so wie im Beispiel gezeigt:


Public Function AnzArrayDim(Feld) As Long
    AnzArrayDim = -1
    If Not IsArray(Feld) Then Exit Function
   
    On Error GoTo AusstiegBeiLaufzeitFehler
    Do
        AnzArrayDim = AnzArrayDim + 1
    Loop While UBound(Feld, AnzArrayDim + 1) >= 0
AusstiegBeiLaufzeitFehler:
End Function

Ist der Rückgabewert -1, so handelt es sich nicht um ein Array. Wurde das Array noch nicht dimensioniert, so ist der Rückgabewert 0 und sonst erhält man die Anzahl der Dimensionen.

Auf diese Funktion aufbauend, lassen sich auch die Elemente des Arrays zählen:


Public Function AnzElem(Feld) As Long
    Dim AnzDim As Long, i As Long
   
    AnzDim = AnzArrayDim(Feld)
    AnzElem = IIf(AnzDim < 1, AnzDim, 1)
    For i = 1 To AnzDim
        AnzElem = AnzElem * (UBound(Feld, i) - LBound(Feld, i) + 1)
    Next i
End Function

Code Snippets

Hier ein paar kleine Code-Schnipsel zur schnellen Verwendung.

Das erste Beispiel passt in eine Programmzeile und erinnert ein wenig an die VBA-Funktion Choose:


MsgBox Array("Mo", "Di", "Mi", "Do", "Fr", "Sa", "So")(Weekday(Now, vbMonday)  1)

Diese Zeile gibt die deutsche Kurzbezeichnung für den Wochentag von heute (Quelle: Funktion Now, welche das Systemdatum des Rechners liefert) heraus.

Die möglichen Ausgabewerte werden als Elemente in das Array eingetragen. Sofort hinter dem Array kommt die Klammer, in der der Index zur Auswahl erwartet wird. Diesen Index liefert die Funktion Weekday() aus dem Tagesdatum Now(), wobei in Deutschland der Montag als erster Tag in der Woche gilt. Da Weekday() Werte zwischen 1-7 zurückgibt, muss vom Ergebnis von Weekday() noch 1 abgezogen werden.

Quiz für Codeversteher: Was könnte ein Unterschied zu Choose sein?

In diesem Falle macht es keinen großen Unterschied. Aber man könnte hier mit verschachtelten Arrays arbeiten, was komplexere Auswahlen erlaubt.

Im folgenden Beispiel wird ein eindimensionales Array um ein Element vergrößert:


ReDim Preserve Werte(LBound(Werte) To UBound(Werte) + 1)

Voraussetzung ist, dass die Dimension erst durch ein ReDim angelegt wurde. Das Schlüsselwort Preserve sorgt dafür, dass die vorhandenen Elemente nicht gelöscht werden.

Quiz für Codeversteher: Warum muss in diese Beispiel das Array Werte() eindimensional sein, wenn es mit ReDim Werte(UBound(Werte) + 1) bearbeitet wird?

Arrays können nur in der letzten Dimension verändert werden und da weder bei LBound() noch bei UBound() eine Dimension angegeben wurde, nimmt VBA den Default-Wert 1 an. Das Array ist also eindimensional.

Empfehlungen und Quellen

Hier ein paar Tipps, um sicher mit Arrays zu arbeiten:

Es gibt verschiedene Beispielprogramme für Arrays:

Was fehlt oder nicht geht

In Excel gibt es einige versteckte Begrenzungen, die teilweise undokumentiert sind. Speziell die von Arrays kann man sich beiXL-Limits ansehen, wobei dort der Begriff Arrays etwas weiter gefasst wird, als in meinem Beitrag hier.

Einige Funktionen in VBA fehlen leider, sodass sie per Programm nachgerüstet werden müssen. Zu diesen Funktionen gehören:

Ich habe den Eindruck, dass Microsoft es noch nie verstanden hat, nützliche Konzepte weiterzuentwickeln. So erscheint mir die Office-Entwicklung (von Randfeatures abgesehen) praktisch tot und eine Behebung der hier genannten Lücken im Array-Konzept ist für VBA nicht mehr zu erwarten. Statt dessen versucht Microsoft offenbar, mit neuen Konzepten Kunden zu halten (Ribbons, Cloud, 64-Bit mit Inkompatibilität) und wirft Bewährtes und Beliebtes über Bord.

Dieses strategiefreie und meiner Meinung nach kundenfeindliche Konzept führte dazu, dass ich diesen Beitrag mit LibreOffice Writer und Excel 2000 erstellt habe. Dafür laufen meine Beispiele aber auch auf so ziemlich allen 32-bit Office-Versionen seit Office 2000.