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

VBA UBound en Excel — Tamaño de un array con UBound y LBound (y el desfase por uno)

|

VBA UBound en Excel — Tamaño de un array con UBound y LBound (y el desfase por uno)

TL;DR — En VBA no cuentas un array, se lo preguntas. UBound(a) devuelve el índice válido más alto; LBound(a) devuelve el más bajo. Los arrays están indexados desde cero por defecto, así que Dim a(9) contiene diez elementos indexados de 0 a 9UBound es 9, no 10—. Ese desfase por uno es el fallo de bucle más común con diferencia. El bucle que siempre sobrevive es For i = LBound(a) To UBound(a), y la longitud real es UBound(a) - LBound(a) + 1. Un array no tiene .Length ni .Count.

Sub LoopSafely()
    Dim a() As String
    a = Split("red,green,blue", ",")   ' Split devuelve un array indexado desde 0

    Dim i As Long
    For i = LBound(a) To UBound(a)      ' funciona sean cuales sean los limites
        Debug.Print i, a(i)            ' 0 red / 1 green / 2 blue
    Next i

    Debug.Print "count = " & (UBound(a) - LBound(a) + 1)   ' 3
End Sub

UBound es como lees la forma de un array, y es el compañero de ReDim: dimensionas el array y luego preguntas con qué tamaño acabó. Equivócate con los límites y o bien te saltas el último elemento o te sales del final y caes en el error de ejecución 9. Como distintas fuentes te entregan arrays con distintos índices iniciales, el hábito de preguntar —en lugar de suponer 0 o 1— es lo que mantiene los bucles correctos.

Lo que aprenderás

  • Qué devuelven UBound y LBound, y por qué un array no tiene propiedad .Length
  • Por qué los arrays de VBA están indexados desde cero por defecto, y de dónde viene el fallo del desfase por uno
  • El bucle que siempre funciona: For i = LBound(a) To UBound(a)
  • La longitud real de un array: UBound(a) - LBound(a) + 1
  • Obtener el tamaño de cada dimensión de un array 2D con UBound(a, 2)
  • Por qué UBound sobre un array dinámico vacío lanza el error 9, y cómo protegerlo

UBound y LBound: pregunta, no cuentes

UBound (límite superior) devuelve el índice más grande que puedes usar legalmente; LBound (límite inferior) devuelve el más pequeño. Juntos describen la forma del array. Los arrays de VBA deliberadamente no tienen .Length ni .Count —los límites son la fuente de la verdad, y los lees en tiempo de ejecución—:

Dim a(1 To 5) As Long
Debug.Print LBound(a)   ' 1
Debug.Print UBound(a)   ' 5

¿Por qué preguntar en vez de recordar? Porque el array sobre el que iteras a menudo se dimensionó en otro sitio —por un ReDim, por Split, al leer un rango— y codificar a mano el tamaño que crees que tiene es justo como los bucles se desincronizan de la realidad.

Indexado desde cero por defecto: dónde vive el desfase por uno

A menos que digas lo contrario, un array de VBA empieza en el índice 0. Así que Dim a(9) no te da nueve elementos —te da diez, indexados del 0 al 9—:

Dim a(9) As Long        ' diez elementos: a(0) .. a(9)
Debug.Print LBound(a)   ' 0
Debug.Print UBound(a)   ' 9  <- no 10

Esta es la raíz del fallo clásico. For i = 1 To UBound(a) se salta en silencio a(0); For i = 0 To 10 da un paso de más y se topa con «Subscript out of range». Puedes forzar arrays indexados desde 1 para todo un módulo con Option Base 1, o declarar límites explícitos con Dim a(1 To 10) —pero la solución de verdad es dejar de adivinar el inicio por completo—.

El bucle que siempre funciona

Escribe el bucle en términos de los propios límites del array y será correcto sin importar cómo se declaró el array ni de dónde vino:

Dim i As Long
For i = LBound(a) To UBound(a)
    ' ... usa a(i) ...
Next i

Este único hábito elimina toda la familia de errores de desfase por uno. Funciona por igual para un resultado de Split indexado desde 0, un array de Range.Value indexado desde 1, un módulo con Option Base 1 y un Dim a(5 To 12) explícito. Nunca codifiques a mano 0 o 1 como inicio del bucle de un array cuando LBound te dirá la verdad gratis.

La longitud de un array: UBound menos LBound más uno

Como un array puede empezar en cualquier índice, su longitud no es simplemente UBound + 1. La fórmula que siempre es correcta es:

Dim n As Long
n = UBound(a) - LBound(a) + 1

Para un array de diez indexado desde 0, eso es 9 - 0 + 1 = 10; para Dim a(1 To 10), es 10 - 1 + 1 = 10. Usa la fórmula completa y da igual qué base use el array. (La gente busca una propiedad «VBA array length»; no existe —esta expresión es la respuesta—.)

Arrays 2D: un límite por dimensión

Para un array multidimensional, UBound y LBound toman un segundo argumento —sobre qué dimensión estás preguntando—. UBound(a, 1) es la primera dimensión (filas), UBound(a, 2) es la segunda (columnas):

Dim g(1 To 3, 1 To 4) As Long   ' 3 filas, 4 columnas
Debug.Print UBound(g, 1)        ' 3  (filas)
Debug.Print UBound(g, 2)        ' 4  (columnas)

Dim r As Long, c As Long
For r = LBound(g, 1) To UBound(g, 1)
    For c = LBound(g, 2) To UBound(g, 2)
        ' ... usa g(r, c) ...
    Next c
Next r

Un UBound(g) a secas sin número de dimensión devuelve la primera dimensión. Esto importa sobre todo cuando lees una hoja en un array, porque Range.Value siempre te entrega un array 2D indexado desde 1 —preguntar UBound(a, 1) y UBound(a, 2) es como descubres cuántas filas y columnas conseguiste en realidad—.

Protege el array dinámico vacío

Un array dinámico que se ha declarado pero al que nunca se le ha hecho ReDim (o al que se le ha hecho Erase) no tiene límites en absoluto, y llamar a UBound sobre él lanza el error de ejecución 9, «Subscript out of range»:

Dim a() As Long
Debug.Print UBound(a)   ' error 9 - el array aun no tiene tamano

Si hay alguna posibilidad de que el array esté vacío —pongamos que viene de un Filter que no encontró nada— protégelo antes de iterar. Un pequeño ayudante mantiene limpio el código que llama:

Function IsAllocated(a As Variant) As Boolean
    On Error Resume Next
    IsAllocated = (LBound(a) <= UBound(a))
    On Error GoTo 0
End Function

Después, If IsAllocated(a) Then For i = LBound(a) To UBound(a) .... Este es el primo (para arrays) de proteger un objeto con Is Nothing antes de usarlo.

Cómo te ayuda ExcelMaster

UBound es una función pequeña con un gran cometido —decirle a un bucle exactamente dónde parar— y los errores a su alrededor (suponer 0 o 1, olvidar que el array está vacío, codificar a mano una longitud) son precisamente los que hacen que una macro se salte la última fila o falle en un caso límite.

ExcelMaster te deja describir la tarea —«mete el rango usado en un array, suma cada columna, escribe los totales debajo»— y itera de LBound a UBound en la dimensión correcta, calcula la longitud correctamente, y protege un array que podría haber vuelto vacío, así que la macro acierta en la primera fila, en la última fila y en el caso vacío. Tú conservas el libro y el código.

Preguntas frecuentes

¿Qué hace UBound en VBA?

UBound devuelve el índice válido más alto de un array —el número más grande que puedes usar dentro de los corchetes—. Para Dim a(1 To 5), UBound(a) es 5; para un Dim a(9) indexado desde cero, es 9. Combínalo con LBound, que devuelve el índice más bajo, para iterar sobre un array sin codificar a mano su tamaño.

¿Cómo obtengo la longitud de un array en VBA?

Un array de VBA no tiene propiedad .Length ni .Count. Calcula el recuento como UBound(a) - LBound(a) + 1. La fórmula completa es necesaria porque un array de VBA puede empezar en el índice 0, 1, o cualquier límite inferior explícito, así que UBound + 1 solo es correcto para los arrays indexados desde cero.

¿Por qué obtengo «Subscript out of range» con UBound?

Normalmente porque el array está vacío —un array dinámico que se declaró con Dim a() pero al que nunca se le hizo ReDim, o al que se le hizo Erase, no tiene límites, así que UBound lanza el error de ejecución 9—. Protégelo con un pequeño ayudante IsAllocated antes de iterar. También ves el error 9 cuando un bucle se pasa de UBound, por ejemplo For i = 0 To 10 sobre un array de diez elementos indexado desde cero.

¿Los arrays de VBA están indexados desde cero o desde uno?

Indexados desde cero por defecto: Dim a(9) contiene diez elementos indexados de 0 a 9. Puedes hacer que un módulo esté indexado desde uno con Option Base 1, o fijar límites explícitos con Dim a(1 To 10). Los arrays devueltos por Split y Array están indexados desde cero; un array leído de Range.Value está indexado desde uno. Como varía, itera de LBound a UBound en lugar de suponer.

¿Cómo averiguo el tamaño de un array 2D en VBA?

Pasa el número de dimensión como segundo argumento: UBound(a, 1) para la primera dimensión (filas) y UBound(a, 2) para la segunda (columnas), con LBound(a, 1) y LBound(a, 2) para los límites inferiores. Así es como lees la forma de un array 2D devuelto por Range.Value.

Probado en

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

Guías relacionadas: VBA ReDim · VBA 2D Arrays · VBA Array · VBA For Loop · VBA Split