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í queDim a(9)contiene diez elementos indexados de0a9—UBoundes9, no10—. Ese desfase por uno es el fallo de bucle más común con diferencia. El bucle que siempre sobrevive esFor i = LBound(a) To UBound(a), y la longitud real esUBound(a) - LBound(a) + 1. Un array no tiene.Lengthni.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
UBoundyLBound, 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é
UBoundsobre 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
