Comment déclarer et remplir un tableau VBA dans Excel
Un tableau VBA est une variable unique qui range plusieurs valeurs sous le même nom, chacune retrouvée par son numéro d’ordre. Là où douze variables demanderaient douze déclarations, Dim mois(1 To 12) As String crée douze cases d’un coup, et une seule boucle les parcourt toutes.
Le mot tableau désigne pourtant plusieurs choses différentes dans Excel, et la confusion commence là. Celui du langage VBA vit en mémoire, invisible dans la feuille, alors que le tableau Excel créé par Ctrl+L, que VBA appelle un ListObject, reste une plage de cellules qu’on voit à l’écran. Les sections qui suivent traitent le premier, puis montrent comment charger le second dedans, sur un classeur de dix mille ventes.
Tableaux VBA
Télécharge le classeur de l’article pour refaire chaque étape sur les mêmes données.
Déclarer un tableau en VBA et fixer ses bornes
Un tableau se déclare comme une variable ordinaire, avec des parenthèses qui annoncent le nombre de cases. La forme 1 To 12 fixe le premier et le dernier indice, si bien qu’on lit la taille du tableau dans sa déclaration. Le type qui suit, As String ou As Long, s’applique alors à toutes les cases à la fois.
Dim mois(1 To 12) As Stringmois(1) = "janvier"mois(12) = "décembre"
Compter les cases d’un tableau déclaré sans borne
La déclaration Dim fixe(10) ne crée pas dix cases mais onze, parce que la numérotation commence à zéro tant qu’on ne demande rien d’autre. La case fixe(0) existe donc bel et bien, et une boucle qui part de 1 laisse une valeur derrière elle sans que rien ne le signale. C’est la raison pour laquelle la forme 1 To 10 vaut mieux partout, puisqu’elle dit à la lecture ce que le tableau contient.
Changer la base avec Option Base 1
L’instruction Option Base 1, écrite tout en haut d’un module, fait commencer à 1 les tableaux que ce module déclare sans borne. Dim t(10) y donne alors dix cases numérotées de 1 à 10, et la fonction Array suit la même règle. La fonction Split, elle, n’en tient aucun compte et rend toujours un tableau qui commence à zéro, ce qui rend ce réglage plus trompeur qu’utile.
Remplir un tableau et le parcourir avec UBound
Chaque case se remplit par son indice, puis se relit de la même façon, exactement comme on écrirait dans une variable ordinaire. Pour parcourir le tableau sans recopier ses bornes, LBound donne le premier indice et UBound le dernier, quelle que soit la déclaration. La boucle survit ainsi à un tableau qui change de taille, ce qui arrive dès qu’on le redimensionne en cours de route.
Dim mois(1 To 12) As StringDim i As LongFor i = LBound(mois) To UBound(mois)mois(i) = "mois numéro " & iNext i
VBA ne propose aucune propriété qui donne directement le nombre d’éléments, et le calcul se fait à partir des bornes, par UBound(mois) - LBound(mois) + 1. Sur un tableau déclaré en 1 To 12, UBound suffit d’ailleurs à lui seul, puisque le dernier indice est aussi le nombre de cases.
Créer un tableau en une ligne avec la fonction Array
La fonction Array fabrique un tableau à partir des valeurs qu’on lui énumère, sans déclaration de taille ni boucle de remplissage. La variable qui le reçoit doit être un Variant, et le tableau produit commence à zéro, même quand le reste du module travaille à partir de 1. C’est la bonne forme pour une liste courte et figée, comme des noms de jours ou une série de codes.
Dim jours As Variantjours = Array("lundi", "mardi", "mercredi")MsgBox jours(0) ' lundi, car la première case porte le numéro 0
Redimensionner un tableau dynamique avec ReDim
Un tableau déclaré avec des parenthèses vides n’a pas encore de taille, et ReDim la lui donne au moment où on la connaît. C’est ce qu’il faut dès que le nombre de valeurs dépend des données, comme le nombre de lignes d’un export qui change chaque mois. Appliquer ReDim à un tableau déjà déclaré avec des bornes ne compile pas et affiche « Tableau déjà dimensionné ».
Dim fournisseurs() As StringDim combien As Longcombien = 5ReDim fournisseurs(1 To combien)
Garder les valeurs déjà rangées avec Preserve
Un ReDim employé seul vide le tableau qu’il redimensionne, et les valeurs déjà rangées disparaissent sans le moindre avertissement. Le mot Preserve les conserve, ce qui permet d’agrandir un tableau au fil d’une boucle qui découvre ses données. Le faire rétrécir garde en revanche les premières cases et perd les dernières, celles qui sortent du nouveau domaine.
Dim mois() As StringReDim mois(1 To 2)mois(1) = "janvier"ReDim Preserve mois(1 To 4) ' mois(1) contient toujours janvierReDim mois(1 To 4) ' sans Preserve, mois(1) est redevenu vide
Pourquoi Preserve refuse de changer la première dimension
Sur un tableau à deux dimensions, Preserve n’accepte de changer que la dernière, c’est-à-dire le nombre de colonnes. Toucher à la première lève l’erreur 9, « L’indice n’appartient pas à la sélection », au moment où la macro s’exécute. Quand ce sont les lignes qui doivent grandir, on range donc les lignes en deuxième dimension et on transpose à la fin, ou bien l’on redimensionne un tableau neuf avant d’y recopier l’ancien.
Dim grille() As LongReDim grille(1 To 3, 1 To 2)ReDim Preserve grille(1 To 3, 1 To 5) ' la dernière dimension, acceptéReDim Preserve grille(1 To 8, 1 To 5) ' la première, erreur 9

Ranger des lignes et des colonnes dans un tableau à 2 dimensions
Un tableau à deux dimensions range ses valeurs comme une grille, avec un numéro de ligne et un numéro de colonne. La déclaration donne les deux couples de bornes d’un coup, et chaque case s’atteint ensuite par ses deux indices, dans ce même ordre. UBound prend alors un second argument, le numéro de la dimension, faute de quoi il rend toujours celle des lignes.
Dim grille(1 To 3, 1 To 2) As Longgrille(1, 1) = 7grille(3, 2) = 12MsgBox UBound(grille, 1) & " lignes sur " & UBound(grille, 2) & " colonnes"
Charger une plage de cellules dans un tableau VBA
C’est l’usage qui rend les tableaux vraiment utiles dans Excel, parce qu’une plage entière se lit en une seule instruction. Range("A2:E6").Value rend un tableau à deux dimensions dont la première ligne porte le numéro 1 et la première colonne le numéro 1, jamais zéro. La variable qui le reçoit est un Variant, puisqu’elle accueille le tableau tout fait plutôt qu’une valeur isolée.
L’onglet Ventes du classeur d’exemple, dont la colonne Total est vide
| A | B | C | D | E | |
|---|---|---|---|---|---|
| 1 | Date | Produit | Quantité | Prix unitaire | Total |
| 2 | 05/01/2026 | Souris ergonomique | 38 | 24,50 € | |
| 3 | 05/01/2026 | Tapis de souris | 21 | 12,90 € | |
| 4 | 05/01/2026 | Enceinte de bureau | 25 | 44,50 € | |
| 5 | 05/01/2026 | Webcam HD | 39 | 54,90 € | |
| 6 | 05/01/2026 | Casque audio | 8 | 79,00 € |

Dim ventes As Variantventes = ThisWorkbook.Worksheets("Ventes").Range("A2:E6").ValueMsgBox ventes(1, 2) ' Souris ergonomique, ligne 1 et colonne 2 du tableau
Une cellule seule échappe à cette règle, et Range("B2").Value rend simplement sa valeur plutôt qu’un tableau d’une case. Une plage d’une seule ligne ou d’une seule colonne, en revanche, reste un tableau à deux dimensions dont l’une ne compte qu’un élément. C’est le piège qui fait échouer une macro le jour où l’export ne contient plus qu’une ligne, et il se referme en comptant les cellules de la plage avant de la lire.
Renvoyer le tableau dans la feuille d’un seul coup
L’écriture emprunte le même chemin dans l’autre sens, en affectant le tableau à une plage de la même taille. Resize fabrique cette plage à partir des bornes du tableau, ce qui évite de compter les lignes à la main et de se tromper d’une. Une plage trop petite tronque les valeurs sans rien dire, quand une plage trop grande remplit les cases en trop avec l’erreur #N/A.
Dim totaux(1 To 3, 1 To 1) As Doubletotaux(1, 1) = 931ThisWorkbook.Worksheets("Ventes").Range("E2").Resize(3, 1).Value = totaux
Écrire un tableau à une dimension dans une colonne
Un tableau à une dimension écrit dans une colonne donne un résultat inattendu, car Excel le lit comme une ligne. L’instruction Range("H1:H3").Value = Array(10, 20, 30) écrit ainsi 10 dans les trois cellules, sans erreur ni avertissement. Application.Transpose remet le tableau debout, et les trois valeurs se rangent alors chacune sur sa ligne.
C’est l’un des pièges les plus coûteux du langage, parce qu’aucun message n’apparaît et que la colonne se remplit quand même. Écris Range("H1:H3").Value = Application.Transpose(Array(10, 20, 30)) pour obtenir 10, 20 et 30 sur trois lignes, et réserve la forme sans Transpose à une plage horizontale.
Lire un tableau de la feuille (ListObject) dans un tableau VBA
Le tableau de la feuille, celui que Ctrl+L crée et qui porte un nom, s’appelle un ListObject en VBA, et ses lignes de données forment une plage comme une autre. DataBodyRange la désigne sans la ligne d’en-tête, en commençant à la première ligne de données, si bien que le tableau en mémoire ne contient rien d’autre. Son mérite est de suivre la taille du fichier, alors qu’une adresse écrite en dur oublie les lignes ajoutées depuis.
Dim donnees As RangeDim ventes As VariantSet donnees = ThisWorkbook.Worksheets("Ventes").ListObjects("tVentes").DataBodyRangeventes = donnees.ValueMsgBox UBound(ventes, 1) & " ventes sur " & UBound(ventes, 2) & " colonnes"
Les colonnes gardent leur ordre d’affichage, donc ventes(1, 3) désigne la quantité de la première vente, troisième colonne du tableau. Pour ne pas dépendre de cet ordre, ListColumns("Quantité").Index donne le numéro d’une colonne à partir de son en-tête, et la macro survit alors à une colonne déplacée. Reste un troisième sens du mot, le tableau dynamique qu’une formule comme FILTRE déverse dans les cellules voisines, qui n’a rien à voir avec le langage.
Accélérer une macro en travaillant en mémoire
Une boucle qui lit et écrit une cellule à la fois franchit la frontière entre VBA et la feuille à chaque tour, et c’est ce passage qui coûte, pas le calcul lui-même. Charger la plage dans un tableau, calculer en mémoire puis réécrire d’un coup ne la franchit que deux fois, quel que soit le nombre de lignes. La macro qui suit remplit ainsi la colonne Total des dix mille ventes du classeur d’exemple.
Sub Remplir_les_totaux()Dim donnees As RangeDim ventes As VariantDim totaux() As DoubleDim ligne As LongSet donnees = ThisWorkbook.Worksheets("Ventes").ListObjects("tVentes").DataBodyRangeventes = donnees.ValueReDim totaux(1 To UBound(ventes, 1), 1 To 1)For ligne = 1 To UBound(ventes, 1)totaux(ligne, 1) = ventes(ligne, 3) * ventes(ligne, 4)Next lignedonnees.Columns(5).Value = totauxEnd Sub
- 01Ouvre le classeur d’exemple, puis l’éditeur VBA avec Alt+F11.
- 02Choisis Module dans le menu Insertion, puis colle les deux macros de l’article dans la fenêtre qui s’ouvre.

Le code collé dans le module, prêt à être enregistré. - 03Enregistre le classeur au format Classeur Excel (prenant en charge les macros).
- 04Reviens dans Excel, appuie sur Alt+F8, choisis Remplir_les_totaux et clique sur Exécuter.

Les deux macros du module apparaissent dans la liste.

La colonne Total se remplit alors sur les dix mille lignes du tableau, et rien ne s’écrit sous le tableau, puisque DataBodyRange s’arrête à la dernière ligne de données. La seconde macro du fichier reprend le même calcul deux fois, une cellule à la fois puis par un tableau, et affiche les deux durées.
Sub Comparer_les_deux_methodes()Dim donnees As RangeDim ventes As VariantDim totaux() As DoubleDim ligne As LongDim depart As SingleDim uneCellule As SingleDim enMemoire As SingleSet donnees = ThisWorkbook.Worksheets("Ventes").ListObjects("tVentes").DataBodyRangeApplication.ScreenUpdating = False' Une cellule à la fois, soit deux lectures et une écriture par lignedonnees.Columns(5).ClearContentsdepart = TimerFor ligne = 1 To donnees.Rows.Countdonnees.Cells(ligne, 5).Value = donnees.Cells(ligne, 3).Value * donnees.Cells(ligne, 4).ValueNext ligneuneCellule = Timer - depart' Le même calcul, par un tableau en mémoiredonnees.Columns(5).ClearContentsdepart = Timerventes = donnees.ValueReDim totaux(1 To UBound(ventes, 1), 1 To 1)For ligne = 1 To UBound(ventes, 1)totaux(ligne, 1) = ventes(ligne, 3) * ventes(ligne, 4)Next lignedonnees.Columns(5).Value = totauxenMemoire = Timer - departApplication.ScreenUpdating = TrueMsgBox "Cellule par cellule : " & Format(uneCellule, "0.000") & " s" & vbCrLf & _"Tableau en mémoire : " & Format(enMemoire, "0.000") & " s", _vbInformation, "10 000 lignes"End Sub

Le chronomètre du langage s’appelle Timer, et la différence entre deux relevés donne la durée écoulée en secondes. Sur les dix mille lignes du classeur, la boucle cellule par cellule demande un peu moins d’une demi-seconde quand la version en mémoire tient sous les deux centièmes, soit au moins vingt fois plus vite. Les deux méthodes travaillent écran figé par ScreenUpdating, donc l’écart ne vient pas du rafraîchissement de la feuille mais bien des allers-retours.
Le gain grandit avec le fichier, et il devient décisif dès qu’une macro fait plus qu’une multiplication par ligne. Pour le reste du geste, l’article sur la boucle For en VBA détaille la boucle elle-même, celui sur l’objet Range la façon de désigner la plage à lire, et l’installation d’une macro ce qui se passe quand Excel bloque un fichier téléchargé.
Questions fréquentes
Un tableau déclaré sans borne commence à 0, si bien que Dim fixe(10) donne onze cases numérotées de 0 à 10. La déclaration Dim fixe(1 To 10) lève l’ambiguïté, et un tableau lu depuis une plage de cellules par Range(...).Value commence toujours à 1, dans ses deux dimensions.
Le langage n’offre aucune propriété qui donne cette taille, et le calcul passe par les bornes, avec UBound(t) - LBound(t) + 1. Sur un tableau à deux dimensions, le second argument choisit la dimension mesurée, UBound(t, 2) rendant le dernier numéro de colonne.
L’instruction Erase s’en charge, mais elle n’agit pas de la même façon selon la déclaration. Sur un tableau dynamique, elle libère la mémoire et le tableau n’a plus de bornes du tout, si bien qu’un UBound lève ensuite l’erreur 9. Sur un tableau déclaré avec des bornes, elle remet seulement chaque case à sa valeur de départ, zéro pour un nombre et une chaîne vide pour du texte.
Application.Match travaille aussi bien sur un tableau que sur une plage, et rend la position de la valeur cherchée. Attention au décalage, car cette position part toujours de 1, y compris sur un tableau qui commence à zéro, où le deuxième élément est rendu comme la position 2 alors qu’il porte l’indice 1.
Aucune instruction native ne trie un tableau, il faut donc écrire la comparaison soi-même, en parcourant les cases deux à deux et en échangeant celles qui sont dans le désordre. L’autre chemin consiste à écrire le tableau dans une feuille et à laisser Excel faire le travail, comme le montre l’article sur le tri d’une colonne, puis à relire la plage triée.
Un tableau a une taille qu’on fixe et des cases numérotées, alors qu’une Collection grandit toute seule à chaque Add sans qu’on la redimensionne. Le tableau reste le plus rapide pour traiter des milliers de lignes venues d’une feuille, quand la Collection, ou le Dictionary, convient mieux à une liste qu’on remplit au fil de l’eau ou qu’on retrouve par une clé.
