🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA Cells frente a Range en Excel — referenciar celdas por número (y por qué Cells(1, 2) es B1, no A2)

|

VBA Cells frente a Range en Excel — referenciar celdas por número (y por qué Cells(1, 2) es B1, no A2)

TL;DRCells es Range direccionado por número. Range("A1") nombra una celda con una cadena en el orden en que la lees (letra de columna y luego fila). Cells(row, column) nombra una celda con dos enteros en el orden en que Excel la guardaprimero la fila, luego la columna — y por eso Cells(1, 2) es B1, no A2. Como ambas coordenadas son números que puedes calcular, Cells es la referencia hecha para los bucles. Usa Range para las regiones que escribes por nombre, Cells para las posiciones que calculas, y Range(Cells(1, 1), Cells(n, k)) para construir un bloque a partir de esquinas calculadas.

Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Data")
' Range nombra una region fija; Cells calcula una posicion con la fila primero.
ws.Range("A1").Value = "Header"          ' la forma humana - una direccion escrita
ws.Cells(2, 1).Value = "First row"       ' fila 2, columna 1 = A2 (NO B2)

Referirse a celdas es lo primero que hace toda macro, y Cells frente a Range es el primer sitio donde la gente tropieza. La confusión no es que una sea mejor — es que direccionan las celdas de forma distinta, y confundir el orden escribe en el lugar equivocado sin avisar. Esta guía se construye sobre una sola idea que vuelve predecible todo lo demás: Cells es el gemelo numérico de Range, y sus argumentos van con la fila primero. En cuanto lo tienes claro, tanto los patrones de bucle como los errores de orientación cobran sentido.

Lo que aprenderás

  • El modelo mental — Cells es Range direccionado con dos números en vez de una cadena
  • La regla más importante — Cells(row, column), así que Cells(1, 2) es B1, no A2
  • Construir un bloque a partir de esquinas calculadas con Range(Cells(...), Cells(...))
  • Por qué un Cells a secas, sin índice, significa cada celda de la hoja
  • Indexar dentro de un rango existente con rng.Cells(i) y su orden por filas
  • Cuándo echar mano de Range y cuándo de Cells — y por qué no meterlo en un bucle

El modelo mental: Cells es Range direccionado por número

Una referencia de celda tiene que responder una sola pregunta — ¿qué celda? — y VBA te da dos formas de responderla. Range("A1") responde con una cadena en notación de hoja de cálculo: primero la letra de columna, luego el número de fila, exactamente como tu ojo recorre la cuadrícula. Cells(1, 1) responde con dos enteros: la fila y luego la columna.

ws.Range("B3").Value = 10    ' direccion de texto: columna B, fila 3
ws.Cells(3, 2).Value = 10    ' la misma celda, como numeros: fila 3, columna 2

Apuntan a la misma celda. La diferencia que importa es qué puedes hacer con la dirección: una cadena como "B3" es texto fijo, pero Cells(3, 2) son dos números que puedes calcular. Esa es toda la razón de que exista Cells — es la referencia que puedes gobernar desde un contador, un UBound o una fila que encontraste en tiempo de ejecución. Range es para la región que conoces por su nombre; Cells es para la posición que resuelves en el código.

La regla más importante: Cells es (fila, columna), así que Cells(1, 2) es B1

Esta es la línea que pilla a todos los que vienen de escribir "A2". Los argumentos van primero la fila, luego la columna — abajo antes que a lo ancho — que es lo contrario del orden «letra y luego número» que lees en la hoja:

ws.Cells(1, 2).Value = "here"   ' fila 1, columna 2  =>  B1   (NO A2)
ws.Cells(2, 1).Value = "there"  ' fila 2, columna 1  =>  A2

Si esperabas que Cells(1, 2) fuera A2 porque «A es la columna 1 y 2 es la fila», lo leíste en orden de cuadrícula; VBA lo lee en orden de almacenamiento. Un truco fiable: R antes que C — Row (fila) y luego Column (columna), el mismo orden que la notación R1C1 y los subíndices de una matriz. Invierte esto dentro de un bucle y cada escritura cae desviada un eje, transpuesta por toda la hoja, sin ningún error que te avise. Ante la duda, dilo en voz alta como «fila r, columna c» cada vez.

El argumento de columna también acepta una letra como cadena, algo cómodo cuando conoces la columna pero calculas la fila:

ws.Cells(lastRow + 1, "C").Value = total   ' la fila se calcula, la columna C se conoce

Construir un bloque a partir de esquinas calculadas: Range(Cells, Cells)

Cells direcciona una sola celda, así que por sí mismo no puede describir un bloque. El modismo que desbloquea los rangos dinámicos es pasar dos llamadas a Cells como las esquinas de un Range:

Dim lastRow As Long, lastCol As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row       ' fondo de la columna A
lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
' Construir todo el bloque de datos a partir de dos esquinas calculadas:
Dim data As Range
Set data = ws.Range(ws.Cells(1, 1), ws.Cells(lastRow, lastCol))   ' A1 : ultima celda

Esta es la recompensa de aprender Cells: Range("A1") por sí solo no puede tomar una esquina inferior derecha calculada sin pegar una cadena a mano ("A" & lastRow), lo que se rompe en cuanto tus columnas pasan de la Z. Range(Cells(1, 1), Cells(lastRow, lastCol)) se mantiene numérico y nunca le importa lo anchos que sean los datos. Desde aquí puedes reformar la referencia con Resize o dejar que Excel encuentre el bloque por ti con CurrentRegion.

Un Cells a secas significa toda la hoja

Cells sin índice no es un error ni una sola celda — es la colección de celdas de toda la hoja. Eso es realmente útil para operaciones que abarcan la hoja entera:

ws.Cells.ClearContents          ' borra todos los valores de la hoja
ws.Cells.Font.Name = "Calibri"  ' fija la fuente en todas partes de una vez

Pero también es una trampa: un Cells a secas donde querías una celda concreta actúa sin avisar sobre las 1.048.576 × 16.384 que hay. Si una macro de «dar formato a una celda» reformatea la hoja entera, busca un Cells que perdió su índice. El hábito seguro es darle siempre a Cells su (row, column) salvo que de verdad quieras decir todo.

Cells dentro de un rango, y el índice lineal

Cells no es solo una propiedad de la hoja — también funciona en relación con un rango, donde (1, 1) es la esquina superior izquierda del propio rango, no A1:

Dim blk As Range: Set blk = ws.Range("C5:F20")
blk.Cells(1, 1).Value = "top-left of the block"   ' C5, no A1

También existe una forma de índice único, Cells(i), que recorre las celdas en orden por filas — de izquierda a derecha a lo largo de una fila, y luego baja a la siguiente:

blk.Cells(1).Value   ' C5   (primera celda)
blk.Cells(5).Value   ' C6   (cuatro a lo ancho del bloque de 4, luego salta a la fila siguiente)

La sorpresa aquí es la dirección: Cells(i) cuenta a lo ancho antes que hacia abajo, así que en un bloque de 4 columnas la celda número 5 es el comienzo de la segunda fila, no la quinta fila. Si un recorrido lineal parece saltar al lugar equivocado, casi siempre es este orden por filas frente a una expectativa por columnas.

Range para regiones, Cells para posiciones — y no lo metas en un bucle

La regla limpia no es «prefiere una». Es: usa Range para las regiones que nombras y Cells para las posiciones que calculas. Un área de informe fija, un bloque con nombre, una fila de encabezado que escribes una vez — eso es Range("A1:D1"). Una celda cuya fila o columna calculas en tiempo de ejecución — eso es Cells(r, c). Se combinan: Range(Cells(...), Cells(...)) construye un bloque con forma de rango a partir de esquinas calculadas.

Lo único que hay que resistir es echar mano de Cells dentro de un bucle celda a celda cuando existe una operación sobre todo el rango. Escribir For r = 2 To lastRow: ws.Cells(r, 3).Value = ... toca la hoja una vez por fila y es la razón número uno de que las macros vayan a paso de tortuga. La jugada madura es usar Cells para encontrar las esquinas, construir el Range y actuar sobre todo el bloque de una vez — asigna una matriz a Range(...).Value, o llama a un método nativo (ver WorksheetFunction). Cells es cómo localizas el trabajo; no debería ser cómo haces cada unidad de él.

Cómo ayuda ExcelMaster

Cells frente a Range esconde más decisiones de las que parece: qué argumento es la fila, si un Cells a secas acaba de tocar la hoja entera, si un índice lineal va a lo ancho o hacia abajo, y si un bucle debería haber sido una única escritura sobre todo el rango. Cada giro equivocado es silencioso — un valor desviado un eje, un reformateo de toda la hoja, una macro que va a paso de tortuga.

ExcelMaster te deja describir el resultado en su lugar. Di «rellena la columna C con un acumulado hasta la última fila de datos», y encuentra la última fila, construye Range(Cells(2, 3), Cells(lastRow, 3)) y escribe los valores en una sola pasada — con los argumentos en orden de fila primero, sin un Cells accidental sobre toda la hoja, sin bucle celda a celda. Te quedas con el libro y con el código; te ahorras la pasada en la que las escrituras cayeron transpuestas.

Preguntas frecuentes

¿Cuál es la diferencia entre Cells y Range en Excel VBA?

Ambos se refieren a celdas, pero las direccionan de forma distinta. Range("A1") usa una cadena en notación de hoja de cálculo — letra de columna y luego fila. Cells(1, 1) usa dos números — fila y luego columna. Como los argumentos de Cells son números que puedes calcular, Cells es lo que usas dentro de los bucles y para las posiciones que resuelves en tiempo de ejecución; Range es para las regiones fijas o con nombre que escribes por su nombre. Se combinan como Range(Cells(r1, c1), Cells(r2, c2)) para construir un bloque a partir de esquinas calculadas.

¿Por qué Cells(1, 2) es la celda B1 y no A2?

Porque Cells toma sus argumentos primero la fila, luego la columna — lo contrario del orden «letra y luego número» que lees en la cuadrícula. Cells(1, 2) significa fila 1, columna 2, que es B1. Cells(2, 1) significa fila 2, columna 1, que es A2. Recuerda «R antes que C» (row y luego column), el mismo orden que la notación R1C1.

¿Cómo selecciono un rango de celdas usando Cells en VBA?

Pasa dos llamadas a Cells como las esquinas de un Range: Range(Cells(1, 1), Cells(10, 4)) es A1:D10. Esta es la forma estándar de construir un bloque cuando las esquinas se calculan, por ejemplo Range(Cells(2, 1), Cells(lastRow, lastCol)). Cualifica tanto los Cells como el Range con la misma hoja para que la referencia no apunte a la hoja que resulte estar activa.

¿Qué hace Cells sin argumentos en VBA?

Un Cells a secas (sin índice) se refiere a cada celda de la hoja. Cells.ClearContents borra la hoja entera y Cells.Font.Size = 11 da valor a cada celda. Eso es útil para cambios que abarcan toda la hoja, pero también significa que un Cells que perdió su índice (row, column) actuará sin avisar sobre la hoja entera — así que ponle siempre el índice cuando te refieras a una celda concreta.

¿Puedo usar una letra de columna con Cells en vez de un número?

Sí. El argumento de columna acepta una cadena, así que Cells(2, "B") es lo mismo que Cells(2, 2) (la celda B2). El argumento de fila tiene que ser un número. Usar una letra es cómodo cuando conoces la columna pero calculas la fila, como en Cells(lastRow + 1, "C").

Probado en

Probado en: Excel 365 (Windows 11), VBA 7.1 — última verificación el 15/08/2026.

Guías relacionadas: VBA Range · VBA Resize · VBA CurrentRegion · VBA Offset · VBA Bucle For