TL;DR —
CellsistRange, adressiert per Zahl.Range("A1")benennt eine Zelle mit einem String in Lesereihenfolge (Spaltenbuchstabe, dann Zeile).Cells(row, column)benennt eine Zelle mit zwei Ganzzahlen in der Reihenfolge, in der Excel sie speichert — Zeile zuerst, dann Spalte — weshalbCells(1, 2)gleich B1 ist, nicht A2. Weil beide Koordinaten Zahlen sind, die Sie berechnen können, istCellsder Bezug, der für Schleifen gemacht ist. Nehmen SieRangefür Bereiche, die Sie per Name tippen,Cellsfür Positionen, die Sie berechnen, undRange(Cells(1, 1), Cells(n, k)), um einen Block aus berechneten Ecken zu bauen.
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Data")
' Range benennt einen festen Bereich; Cells berechnet eine Position, Zeile zuerst.
ws.Range("A1").Value = "Header" ' der menschliche Weg - eine getippte Adresse
ws.Cells(2, 1).Value = "First row" ' Zeile 2, Spalte 1 = A2 (NICHT B2)
Auf Zellen zu verweisen ist das Erste, was jedes Makro tut, und Cells gegen Range ist die erste
Stelle, an der man stolpert. Die Verwirrung liegt nicht darin, dass eines besser wäre — sondern
darin, dass sie Zellen unterschiedlich adressieren, und wer die Reihenfolge vertauscht, schreibt
stillschweigend an die falsche Stelle. Diese Anleitung ruht auf einer Idee, die den Rest
vorhersagbar macht: Cells ist der numerische Zwilling von Range, und seine Argumente laufen
Zeile zuerst. Sobald Sie das verinnerlicht haben, ergeben sowohl die Schleifenmuster als auch die
Achsen-Vertauschungs-Bugs Sinn.
Was Sie lernen
- Das mentale Modell —
CellsistRange, adressiert mit zwei Zahlen statt einem String - Die wichtigste Regel —
Cells(row, column), also istCells(1, 2)B1, nicht A2 - Einen Block aus berechneten Ecken bauen mit
Range(Cells(...), Cells(...)) - Warum ein blankes
Cellsohne Index jede Zelle des Blatts meint - In einen bestehenden Bereich indizieren mit
rng.Cells(i)und dessen zeilenweiser Reihenfolge - Wann Sie zu
Rangegreifen und wann zuCells— und warum Sie es nicht in einer Schleife tun
Das mentale Modell: Cells ist Range, adressiert per Zahl
Ein Zellbezug muss eine Frage beantworten — welche Zelle? — und VBA gibt Ihnen zwei Wege, das zu
tun. Range("A1") antwortet mit einem String in Tabellennotation: erst der
Spaltenbuchstabe, dann die Zeilennummer, genau so, wie Ihr Auge das Raster abtastet. Cells(1, 1)
antwortet mit zwei Ganzzahlen: erst die Zeile, dann die Spalte.
ws.Range("B3").Value = 10 ' String-Adresse: Spalte B, Zeile 3
ws.Cells(3, 2).Value = 10 ' dieselbe Zelle als Zahlen: Zeile 3, Spalte 2
Sie zeigen auf dieselbe Zelle. Der entscheidende Unterschied ist, was Sie mit der Adresse anfangen
können: ein String wie "B3" ist fester Text, aber Cells(3, 2) sind zwei Zahlen, die Sie
berechnen können. Genau dafür gibt es Cells — es ist der Bezug, den Sie aus einem Zähler, einem
UBound oder einer zur Laufzeit gefundenen Zeile heraus steuern können. Range ist für den Bereich,
den Sie beim Namen kennen; Cells ist für die Position, die Sie im Code ermitteln.
Die wichtigste Regel: Cells ist (Zeile, Spalte), also ist Cells(1, 2) B1
Hier ist die Zeile, die jeden erwischt, der vom Tippen von "A2" kommt. Die Argumente sind erst die
Zeile, dann die Spalte — nach unten vor quer — das Gegenteil der Reihenfolge „Buchstabe, dann Zahl“,
die Sie auf dem Blatt lesen:
ws.Cells(1, 2).Value = "here" ' Zeile 1, Spalte 2 => B1 (NICHT A2)
ws.Cells(2, 1).Value = "there" ' Zeile 2, Spalte 1 => A2
Wenn Sie erwartet haben, dass Cells(1, 2) A2 ist, weil „A ist Spalte 1 und 2 ist die Zeile“, dann
haben Sie es in Rasterreihenfolge gelesen; VBA liest es in Speicherreihenfolge. Eine verlässliche
Merkregel: R vor C — Row (Zeile), dann Column (Spalte), dieselbe Reihenfolge wie in der
R1C1-Notation und bei Matrix-Indizes. Machen Sie das in einer Schleife verkehrt herum, landet jeder
Schreibvorgang eine Achse daneben, quer über das Blatt transponiert, ohne dass ein Fehler Sie warnt.
Sagen Sie im Zweifel jedes Mal laut „Zeile r, Spalte c“.
Das Spaltenargument akzeptiert auch einen Buchstaben als String, was praktisch ist, wenn Sie die Spalte kennen, aber die Zeile berechnen:
ws.Cells(lastRow + 1, "C").Value = total ' Zeile ist berechnet, Spalte C ist bekannt
Einen Block aus berechneten Ecken bauen: Range(Cells, Cells)
Cells adressiert eine einzelne Zelle, kann also für sich genommen keinen Block beschreiben. Das
Idiom, das dynamische Bereiche erschließt, ist, zwei Cells-Aufrufe als Ecken eines Range zu
übergeben:
Dim lastRow As Long, lastCol As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row ' unteres Ende von Spalte A
lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
' Den ganzen Datenblock aus zwei berechneten Ecken bauen:
Dim data As Range
Set data = ws.Range(ws.Cells(1, 1), ws.Cells(lastRow, lastCol)) ' A1 : letzte Zelle
Das ist der Lohn dafür, Cells zu lernen: Range("A1") allein kann keine berechnete untere rechte
Ecke aufnehmen, ohne einen String zusammenzukleben ("A" & lastRow), was in dem Moment bricht, in
dem Ihre Spalten über Z hinausgehen. Range(Cells(1, 1), Cells(lastRow, lastCol)) bleibt numerisch
und schert sich nie darum, wie breit die Daten sind. Von hier aus können Sie den Bezug mit
Resize umformen oder Excel den Block für Sie finden lassen mit
CurrentRegion.
Ein blankes Cells meint das ganze Blatt
Cells ohne Index ist kein Fehler und keine einzelne Zelle — es ist die Zellauflistung des
gesamten Arbeitsblatts. Das ist für blattweite Operationen wirklich nützlich:
ws.Cells.ClearContents ' jeden Wert auf dem Blatt loeschen
ws.Cells.Font.Name = "Calibri" ' die Schriftart ueberall auf einmal setzen
Aber es ist auch eine Falle: ein blankes Cells, wo Sie eine bestimmte Zelle meinten, wirkt
stillschweigend auf alle 1.048.576 × 16.384. Wenn ein Makro, das „eine Zelle formatieren“ soll, das
ganze Blatt umformatiert, suchen Sie nach einem Cells, das seinen Index verloren hat. Die sichere
Gewohnheit ist, Cells immer sein (row, column) zu geben, außer Sie meinen wirklich alles.
Cells innerhalb eines Bereichs und der lineare Index
Cells ist nicht nur eine Eigenschaft des Arbeitsblatts — es funktioniert auch relativ zu einem
Bereich, wobei (1, 1) die obere linke Ecke dieses Bereichs ist und nicht A1:
Dim blk As Range: Set blk = ws.Range("C5:F20")
blk.Cells(1, 1).Value = "top-left of the block" ' C5, nicht A1
Es gibt auch eine Ein-Index-Form, Cells(i), die die Zellen in zeilenweiser Reihenfolge
durchläuft — von links nach rechts durch eine Zeile, dann hinunter zur nächsten:
blk.Cells(1).Value ' C5 (erste Zelle)
blk.Cells(5).Value ' C6 (vier quer durch den 4 Spalten breiten Block, dann Umbruch in die naechste Zeile)
Die Überraschung hier ist die Richtung: Cells(i) zählt quer vor hinunter, in einem Block aus 4
Spalten ist Zelle Nummer 5 also der Anfang der zweiten Zeile, nicht die fünfte Zeile. Wenn ein
linearer Durchlauf an die falsche Stelle zu springen scheint, ist es fast immer diese zeilenweise
Reihenfolge gegen eine spaltenweise Erwartung.
Range für Bereiche, Cells für Positionen — und keine Schleife darum
Die saubere Regel lautet nicht „bevorzuge eines“. Sie lautet: nehmen Sie Range für Bereiche, die
Sie benennen, und Cells für Positionen, die Sie berechnen. Ein fester Berichtsbereich, ein
benannter Block, eine Kopfzeile, die Sie einmal tippen — das ist Range("A1:D1"). Eine Zelle, deren
Zeile oder Spalte Sie zur Laufzeit berechnen — das ist Cells(r, c). Sie lassen sich kombinieren:
Range(Cells(...), Cells(...)) baut einen benannt geformten Block aus berechneten Ecken.
Das Einzige, dem Sie widerstehen sollten, ist zu Cells innerhalb einer Zelle-für-Zelle-Schleife
zu greifen, wenn es eine Operation über den ganzen Bereich gibt. For r = 2 To lastRow: ws.Cells(r, 3).Value = ... zu schreiben fasst das Arbeitsblatt einmal pro Zeile an und ist der
Hauptgrund, warum Makros kriechen. Der reife Zug ist, Cells zu nutzen, um die Ecken zu finden,
den Range zu bauen und einmal auf den ganzen Block zu wirken — ein Array an Range(...).Value
zuzuweisen oder eine native Methode aufzurufen (siehe
WorksheetFunction). Cells ist, wie Sie die Arbeit verorten; es
sollte nicht sein, wie Sie jede Einheit davon erledigen.
Wie ExcelMaster hilft
Cells gegen Range verbirgt mehr Entscheidungen, als es scheint: welches Argument die Zeile ist, ob
ein blankes Cells gerade das ganze Blatt angefasst hat, ob ein linearer Index quer oder hinunter
läuft und ob eine Schleife ein einziger Schreibvorgang über den ganzen Bereich hätte sein sollen.
Jede falsche Abzweigung ist leise — ein Wert eine Achse daneben, eine blattweite Umformatierung, ein
Makro, das kriecht.
ExcelMaster lässt Sie stattdessen
das Ergebnis beschreiben. Sagen Sie „fülle Spalte C mit einer laufenden Summe bis zur letzten
Datenzeile“, und es findet die letzte Zeile, baut Range(Cells(2, 3), Cells(lastRow, 3)) und schreibt
die Werte in einem Durchgang — die Argumente Zeile zuerst korrekt, kein versehentliches blattweites
Cells, keine Schleife pro Zelle. Sie behalten die Arbeitsmappe und den Code; Sie sparen sich den
Durchgang, in dem die Schreibvorgänge transponiert landeten.
Häufig gestellte Fragen
Was ist der Unterschied zwischen Cells und Range in Excel VBA?
Beide verweisen auf Zellen, aber sie adressieren sie unterschiedlich. Range("A1") nutzt einen
String in Tabellennotation — Spaltenbuchstabe, dann Zeile. Cells(1, 1) nutzt zwei Zahlen —
Zeile, dann Spalte. Weil die Argumente von Cells Zahlen sind, die Sie berechnen können, ist Cells
das, was Sie in Schleifen und für zur Laufzeit ermittelte Positionen nehmen; Range ist für feste
oder benannte Bereiche, die Sie beim Namen tippen. Sie kombinieren sich als
Range(Cells(r1, c1), Cells(r2, c2)), um einen Block aus berechneten Ecken zu bauen.
Warum ist Cells(1, 2) die Zelle B1 und nicht A2?
Weil Cells seine Argumente erst die Zeile, dann die Spalte nimmt — das Gegenteil der Reihenfolge
„Buchstabe, dann Zahl“, die Sie auf dem Raster lesen. Cells(1, 2) bedeutet Zeile 1, Spalte 2, also
B1. Cells(2, 1) bedeutet Zeile 2, Spalte 1, also A2. Merken Sie sich „R vor C“ (Row, dann Column),
dieselbe Reihenfolge wie in der R1C1-Notation.
Wie wähle ich mit Cells einen Zellbereich in VBA aus?
Übergeben Sie zwei Cells-Aufrufe als Ecken eines Range: Range(Cells(1, 1), Cells(10, 4)) ist
A1:D10. Das ist der Standardweg, einen Block zu bauen, wenn die Ecken berechnet sind, zum Beispiel
Range(Cells(2, 1), Cells(lastRow, lastCol)). Qualifizieren Sie sowohl Cells als auch den Range
mit demselben Arbeitsblatt, damit der Bezug nicht auf das gerade aktive Blatt zeigt.
Was macht Cells ohne Argumente in VBA?
Ein blankes Cells (ohne Index) verweist auf jede Zelle des Arbeitsblatts. Cells.ClearContents
leert das ganze Blatt, und Cells.Font.Size = 11 setzt jede Zelle. Das ist für blattweite Änderungen
nützlich, bedeutet aber auch, dass ein Cells, das seinen (row, column)-Index verloren hat,
stillschweigend auf das gesamte Blatt wirkt — indizieren Sie es also immer, wenn Sie eine bestimmte
Zelle meinen.
Kann ich mit Cells einen Spaltenbuchstaben statt einer Zahl nutzen?
Ja. Das Spaltenargument akzeptiert einen String, Cells(2, "B") ist also dasselbe wie Cells(2, 2)
(Zelle B2). Das Zeilenargument muss eine Zahl sein. Ein Buchstabe ist praktisch, wenn Sie die Spalte
kennen, aber die Zeile berechnen, etwa Cells(lastRow + 1, "C").
Getestet in
Getestet in: Excel 365 (Windows 11), VBA 7.1 — zuletzt geprüft am 15.08.2026.
Verwandte Anleitungen: VBA Range · VBA Resize · VBA CurrentRegion · VBA Offset · VBA For-Schleife
