Testé sur : Excel 365 v2509 · Excel 2021 · Excel 2019 · dernière vérification le 07/06/2026
En bref — ByRef passe la variable d'origine, une procédure peut donc la modifier. ByVal passe une copie, l'original est protégé. VBA utilise ByRef par défaut — d'où une variable qui change à votre insu après un appel :
Sub Demo()
Dim n As Long: n = 10
PlusUn n
MsgBox n ' 11 — n a vraiment changé, car ByRef est le défaut
End Sub
Sub PlusUn(ByRef x As Long) ' ByRef : x EST n — écrire dans x écrit dans n
x = x + 1
End Sub
' Remplacez « ByRef » par « ByVal » ci-dessus et MsgBox affiche 10 — l'original est intact.
Ce défaut — ByRef quand vous ne dites rien d'autre — est la source de presque tous les bugs « pourquoi ma variable a-t-elle changé ? » en VBA. Voici comment ne plus jamais être surpris.
Le modèle mental : l'original ou une photocopie
Quand vous passez une variable à une procédure, vous lui donnez accès à vos données. Il y a deux façons de la remettre :
ByRef= vous tendez votre vrai cahier. La procédure écrit sur les pages réelles. Quand elle rend le cahier, vos modifications y sont. (Ref= référence = « un renvoi vers l'original ».)ByVal= vous tendez la photocopie d'une page. La procédure peut gribouiller la copie autant qu'elle veut ; votre cahier ne change pas. (Val= valeur = « un instantané du contenu ».)
Même idée, deux conséquences. Avec ByRef, les changements dans la procédure persistent. Avec ByVal, ils s'évaporent dès que la procédure se termine. Tout le reste est du détail.
La seule règle : le défaut est ByRef, et c'est le piège
Voici le fait qui mord : si vous n'écrivez pas ByVal, VBA utilise ByRef. Ces deux déclarations sont identiques :
Sub Traiter(x As Long) ' aucun mot-clé...
Sub Traiter(ByRef x As Long) ' ...veut dire exactement ceci
Chaque paramètre non marqué que vous avez écrit est donc, en silence, un passage par référence. La plupart du temps cela n'a pas d'importance — mais le jour où un assistant incrémente discrètement le compteur que vous lui avez passé, vous obtenez un bug qui semble impossible : la variable a changé, et la ligne qui l'a changée est dans une autre procédure. Pour qui vient de Python, Java ou C# (qui passent les primitifs par valeur), cela paraît à l'envers — à juste titre : VBA est ici l'exception.
L'habitude qui évite toute cette classe de bugs : écrivez ByVal explicitement sur chaque paramètre, et ne passez à ByRef que lorsque vous voulez vraiment « renvoyer une valeur ». Votre vous-même qui débogue à 18 h vous remerciera.
' Sûr par défaut — les variables des appelants sont protégées :
Sub LogLigne(ByVal numLigne As Long, ByVal libelle As String)
Debug.Print numLigne & " : " & libelle
End Sub
Utiliser ByRef à dessein : plus d'une réponse
ByRef n'est pas une erreur à fuir — c'est un outil. Une Function renvoie exactement une valeur. Quand vous avez vraiment besoin qu'une procédure renvoie deux résultats ou plus, les paramètres de sortie ByRef sont la voie propre et classique :
Sub MinMax(donnees As Range, ByRef lo As Double, ByRef hi As Double)
lo = Application.Min(donnees)
hi = Application.Max(donnees)
End Sub
Sub Utiliser()
Dim bas As Double, haut As Double
MinMax Range("A1:A100"), bas, haut ' remplit LES DEUX variables
MsgBox "Plage : " & bas & " à " & haut
End Sub
MinMax ne renvoie rien par son nom, et pourtant rend deux nombres en écrivant dans les bas et haut de l'appelant. C'est exactement le travail pour lequel ByRef est conçu. La règle : ByVal pour les entrées, ByRef pour les sorties. Marquez-les à dessein, et la signature se documente d'elle-même.
Le piège qui bascule ByRef en ByVal : des parenthèses en trop
Celui-ci est vraiment sournois et tient directement à la façon d'appeler un Sub. Entourer un argument de parenthèses supplémentaires force son évaluation comme expression d'abord — VBA passe donc le résultat, c'est-à-dire une copie, et neutralise ByRef :
PlusUn n ' ByRef comme voulu → n devient 11
PlusUn (n) ' le (n) est évalué d'abord → une COPIE est passée → n reste 10
Call PlusUn(n) ' ici les parenthèses appartiennent à Call → de nouveau ByRef → n devient 11
PlusUn (n) et Call PlusUn(n) se ressemblent mais se comportent à l'opposé. Si un paramètre ByRef refuse mystérieusement de mettre à jour la variable de l'appelant, cherchez une paire de parenthèses égarée autour de l'argument. C'est aussi une astuce volontaire : pour protéger une variable alors que la procédure est ByRef, l'entourer de ( ) est un contournement local rapide.
La nuance que tout le monde rate : les objets ignorent la différence
ByVal protège les valeurs simples — nombres, chaînes, booléens, dates. Il ne copie pas un objet en profondeur. Avec un Range, un Worksheet ou un Workbook, même ByVal passe une copie de la référence, et cette copie pointe encore vers le même objet vivant :
Sub Vider(ByVal rng As Range) ' ByVal, et pourtant...
rng.ClearContents ' ...cela vide bel et bien les cellules de l'appelant
End Sub
On choisit ByVal en croyant qu'il va « protéger » un Range et on s'étonne que les cellules soient quand même effacées. La vérité : ByVal sur un objet vous empêche seulement de réaffecter la variable à un autre objet dans la procédure — il ne protège jamais le contenu de l'objet. Si vous voulez des données intactes, copiez les valeurs vous-même ; le mot-clé ne le fera pas.
Erreurs fréquentes ByRef / ByVal (et la correction)
| Symptôme | Cause | Correction |
|---|---|---|
| Une variable a changé après un appel | Le ByRef par défaut a laissé la procédure la muter |
Déclarer le paramètre ByVal, ou cesser de la modifier dedans |
Le paramètre ByRef ne met pas l'appelant à jour |
Argument entre parenthèses en trop : MonSub (x) |
Retirer les parenthèses (ou utiliser Call MonSub(x)) |
| « Type d'argument ByRef incompatible » | Type de la variable de l'appelant ≠ type du paramètre déclaré | Aligner les types, ou passer ByVal pour que VBA convertisse une copie |
Le Range ByVal a quand même été modifié |
Les objets passent une référence même en ByVal | Copier les valeurs avant l'appel ; le mot-clé ne protège pas le contenu |
| Deux retours nécessaires, globales utilisées | Une Function ne renvoie qu'une valeur | Des paramètres de sortie ByRef — l'outil prévu |
| La récursion sature la mémoire | Une grande valeur passée ByVal est copiée à chaque niveau |
Passer les grands tableaux ByRef pour éviter la copie à chaque appel |
Quand faire circuler les données est la corvée — décrivez le résultat
ByRef/ByVal est typiquement le détail qui fait de VBA un champ de mines : correct, mais délicat, et à une parenthèse égarée d'un bug. Si le vrai objectif est « découpe cette feuille par région et envoie à chaque responsable sa part », vous ne devriez pas raisonner sur la copie des arguments. ExcelMaster Agent prend l'objectif en français courant et produit le résultat — aucun paramètre, aucune convention de passage, aucun piège. Essayer gratuitement →
Guides liés
- VBA Sub dans Excel — Procédures, appel & pourquoi votre macro n'est qu'un Sub
- VBA Function dans Excel — Valeurs de retour & fonctions personnalisées
- VBA MsgBox dans Excel — Oui/Non, boutons & la règle des parenthèses
- VBA Boucle For dans Excel — 8 exemples concrets
FAQ
Quelle différence entre ByRef et ByVal en VBA ?
ByRef passe une référence vers la variable d'origine, les modifications faites dans la procédure persistent donc après son retour. ByVal passe une copie, l'original n'est jamais touché. Le mot-clé décide si une procédure peut modifier la variable de l'appelant.
VBA est-il ByRef ou ByVal par défaut ?
ByRef par défaut. Si vous écrivez un paramètre sans mot-clé — Sub Traiter(x As Long) — VBA le traite comme ByRef, la procédure peut donc changer la variable passée. C'est l'inverse de la plupart des langages modernes et une source fréquente de bugs.
Pourquoi ma variable a-t-elle changé après l'appel d'un Sub ?
Parce que le paramètre était ByRef (le défaut) et que la procédure l'a modifié. Pour protéger la variable, déclarez le paramètre ByVal, qui passe une copie. Sinon, entourez l'argument de parenthèses en trop au point d'appel — MonSub (x) — pour forcer une copie.
Quand utiliser ByRef en VBA ?
Utilisez ByRef à dessein quand une procédure doit renvoyer une valeur à l'appelant — par exemple deux résultats ou plus via des paramètres de sortie. Bonne règle : ByVal pour les entrées, ByRef pour les sorties.
ByVal protège-t-il un objet Range ou Worksheet ?
Non. ByVal ne copie que la référence, pas l'objet ; la copie pointe encore vers le même Range ou Worksheet vivant, modifier son contenu touche donc toujours l'appelant. ByVal sur un objet empêche seulement de réaffecter la variable à un autre objet dans la procédure.
Que fait Call MonSub(x) de différent de MonSub (x) ?
Avec Call, les parenthèses font partie de la syntaxe Call, x est donc toujours passé ByRef. Sans Call, (x) est une expression évaluée d'abord, une copie est donc passée — neutralisant ByRef. Elles se ressemblent mais agissent à l'opposé.
