Quelle est la différence entre
- un paramètre passé par référence
- un paramètre passé par valeur?
Pourriez-vous me donner quelques exemples, s'il vous plaît?
language-agnostic
pass-by-reference
pass-by-value
Déduplicateur
la source
la source
Réponses:
D'abord et avant tout, la distinction «passe par valeur vs passe par référence» telle que définie dans la théorie CS est maintenant obsolète parce que la technique définie à l'origine comme «passe par référence» est depuis tombée en disgrâce et est rarement utilisée maintenant. 1
Les langues plus récentes 2 ont tendance à utiliser une paire de techniques différentes (mais similaires) pour obtenir les mêmes effets (voir ci-dessous), qui est la principale source de confusion.
Une source secondaire de confusion est le fait que dans "passer par référence", "référence" a un sens plus étroit que le terme général "référence" (parce que la phrase la précède).
Maintenant, la définition authentique est:
Lorsqu'un paramètre est transmis par référence , l'appelant et l'appelé utilisent la même variable pour le paramètre. Si l'appelé modifie la variable de paramètre, l'effet est visible pour la variable de l'appelant.
Lorsqu'un paramètre est transmis par valeur , l'appelant et l'appelé ont deux variables indépendantes avec la même valeur. Si l'appelé modifie la variable de paramètre, l'effet n'est pas visible pour l'appelant.
Les points à noter dans cette définition sont:
"Variable" signifie ici la variable de l'appelant (locale ou globale) elle - même - c'est -à -dire que si je passe une variable locale par référence et que je l'affecte, je changerai la variable de l'appelant elle-même, pas par exemple ce qu'elle pointe si c'est un pointeur .
La signification de "référence" dans "passer par référence" . La différence avec le terme général de "référence" est que cette "référence" est temporaire et implicite. Ce que l'appelé obtient essentiellement est une "variable" qui est en quelque sorte "la même" que l'original. La manière dont cet effet est atteint est sans importance (par exemple, le langage peut également exposer certains détails de mise en œuvre - adresses, pointeurs, déréférencement - tout cela n'est pas pertinent; si l'effet net est le suivant, il est transmis par référence).
Maintenant, dans les langages modernes, les variables ont tendance à être de "types de référence" (un autre concept inventé plus tard que "passer par référence" et inspiré par lui), c'est-à-dire que les données réelles des objets sont stockées séparément quelque part (généralement, sur le tas), et seules les "références" y sont conservées dans des variables et passées en paramètres. 3
Le passage d'une telle référence relève de la valeur de passage car la valeur d' une variable est techniquement la référence elle-même, pas l'objet référé. Cependant, l'effet net sur le programme peut être le même que le passage par valeur ou le passage par référence:
Comme vous pouvez le voir, cette paire de techniques est presque la même que celles de la définition, uniquement avec un niveau d'indirection: il suffit de remplacer "variable" par "objet référencé".
Il n'y a pas de nom convenu pour eux, ce qui conduit à des explications tordues comme "appel par valeur où la valeur est une référence". En 1975, Barbara Liskov a suggéré le terme « appel par partage d'objets » (ou parfois simplement «appel par partage») bien qu'il n'ait jamais vraiment fait son chemin. De plus, aucune de ces phrases ne fait de parallèle avec la paire d'origine. Pas étonnant que les anciens termes aient été réutilisés en l'absence de mieux, ce qui a semé la confusion. 4
REMARQUE : Pendant longtemps, cette réponse disait:
Ceci est généralement correct, sauf le sens plus étroit de «référence» - il est à la fois temporaire et implicite (ce n'est pas nécessaire, mais être explicite et / ou persistant sont des fonctionnalités supplémentaires, ne font pas partie de la sémantique de passage par référence , comme expliqué ci-dessus). Une analogie plus étroite serait de vous donner une copie d'un document par rapport à vous invitant à travailler sur l'original.
1 À moins que vous ne programmiez en Fortran ou Visual Basic, ce n'est pas le comportement par défaut, et dans la plupart des langages en utilisation moderne, un véritable appel par référence n'est même pas possible.
2 Un bon nombre d'anciens le soutiennent aussi
3 Dans plusieurs langues modernes, tous les types sont des types de référence. Cette approche a été lancée par le langage CLU en 1975 et a depuis été adoptée par de nombreux autres langages, dont Python et Ruby. Et de nombreux autres langages utilisent une approche hybride, où certains types sont des "types de valeur" et d'autres des "types de référence" - parmi eux, C #, Java et JavaScript.
4 Il n'y a rien de mal à recycler un ancien terme approprié en soi, mais il faut en quelque sorte préciser quelle signification est utilisée à chaque fois. Ne pas le faire est exactement ce qui continue de semer la confusion.
la source
C'est une façon de passer des arguments aux fonctions. Le passage par référence signifie que le paramètre des fonctions appelées sera le même que l'argument passé des appelants (pas la valeur, mais l'identité - la variable elle-même). Passer par valeur signifie que le paramètre des fonctions appelées sera une copie de l'argument passé des appelants. La valeur sera la même, mais l'identité - la variable - est différente. Ainsi, les modifications apportées à un paramètre par la fonction appelée dans un cas modifient l'argument passé et dans l'autre cas modifient simplement la valeur du paramètre dans la fonction appelée (qui n'est qu'une copie). En un rien de temps:
ref
utilisé lors de l'appelant et de la fonction appelée). Jon Skeet a également une belle explication à ce sujet ici .Codes
Puisque mon langage est C ++, je vais l'utiliser ici
Et un exemple en Java ne fera pas de mal:
Wikipédia
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference
Ce gars le cloue à peu près:
http://javadude.com/articles/passbyvalue.htm
la source
De nombreuses réponses ici (et en particulier la réponse la plus élevée) sont en fait incorrectes, car elles ne comprennent pas ce que signifie réellement «appeler par référence». Voici ma tentative de mettre les choses au clair.
TL; DR
En termes simples:
En termes métaphoriques:
Ce que "appeler par valeur" et "appeler par référence" ne signifie pas
Notez que ces deux concepts sont complètement indépendants et orthogonaux du concept des types de référence (qui en Java sont tous les types qui sont des sous-types de
Object
, et en C # tous lesclass
types), ou du concept des types de pointeurs comme en C (qui sont sémantiquement équivalents aux "types de référence" de Java, simplement avec une syntaxe différente).La notion de type de référence correspond à une URL: elle est à la fois une information et une référence (un pointeur , si vous voulez) à d'autres informations. Vous pouvez avoir plusieurs copies d'une URL à différents endroits, et elles ne changent pas vers le site Web auquel elles sont toutes liées; si le site Web est mis à jour, chaque copie d'URL conduira toujours aux informations mises à jour. À l'inverse, la modification de l'URL à un seul endroit n'affectera aucune autre copie écrite de l'URL.
Notez que C ++ a une notion de "références" (par exemple
int&
) qui n'est pas comme les "types de référence" de Java et C #, mais qui est comme "appel par référence". Les "types de référence" de Java et C #, et tous les types en Python, sont comme ce que C et C ++ appellent des "types de pointeurs" (par exempleint*
).OK, voici l'explication plus longue et plus formelle.
Terminologie
Pour commencer, je veux souligner quelques éléments importants de la terminologie, pour aider à clarifier ma réponse et pour nous assurer que nous nous référons tous aux mêmes idées lorsque nous utilisons des mots. (Dans la pratique, je crois que la grande majorité de la confusion sur des sujets tels que ceux-ci découle de l'utilisation de mots de manière à ne pas communiquer pleinement le sens voulu.)
Pour commencer, voici un exemple dans un langage de type C d'une déclaration de fonction:
Et voici un exemple d'appel de cette fonction:
En utilisant cet exemple, je veux définir quelques bits importants de terminologie:
foo
est une fonction déclarée à la ligne 1 (Java insiste pour faire toutes les méthodes des fonctions, mais le concept est le même sans perte de généralité; C et C ++ font une distinction entre déclaration et définition que je n'entrerai pas ici)param
est un paramètre formel defoo
, également déclaré sur la ligne 1arg
est une variable , spécifiquement une variable locale de la fonctionbar
, déclarée et initialisée sur la ligne 2arg
est également un argument pour une invocation spécifique de lafoo
ligne 3Il y a deux ensembles de concepts très importants à distinguer ici. Le premier est la valeur par rapport à la variable :
bar
fonction ci-dessus, après la ligneint arg = 1;
, l'expressionarg
a la valeur1
.final
ou C #readonly
) ou profondément immuable (par exemple en utilisant C ++const
).L'autre paire de concepts importants à distinguer est le paramètre par rapport à l' argument :
Appel par valeur
Dans l' appel par valeur , les paramètres formels de la fonction sont des variables qui sont nouvellement créées pour l'appel de fonction et qui sont initialisées avec les valeurs de leurs arguments.
Cela fonctionne exactement de la même manière que tout autre type de variable est initialisé avec des valeurs. Par exemple:
Ici
arg
etanother_variable
sont des variables complètement indépendantes - leurs valeurs peuvent changer indépendamment les unes des autres. Cependant, au point oùanother_variable
est déclaré, il est initialisé pour contenir la même valeur que celle quiarg
est - qui est1
.Puisqu'il s'agit de variables indépendantes, les modifications à
another_variable
ne pas affecterarg
:C'est exactement la même chose que la relation entre
arg
etparam
dans notre exemple ci-dessus, que je vais répéter ici pour la symétrie:C'est exactement comme si nous avions écrit le code de cette façon:
C'est-à-dire que la caractéristique déterminante de ce que signifie l' appel par valeur est que l'appelé (
foo
dans ce cas) reçoit des valeurs comme arguments, mais a ses propres variables distinctes pour ces valeurs des variables de l'appelant (bar
dans ce cas).Pour en revenir à ma métaphore ci-dessus, si je suis
bar
et vousfoo
, quand je vous appelle, je vous donne un morceau de papier avec une valeur écrite dessus. Vous appelez ce morceau de papierparam
. Cette valeur est une copie de la valeur que j'ai écrite dans mon cahier (mes variables locales), dans une variable que j'appellearg
.(En passant: selon le matériel et le système d'exploitation, il existe différentes conventions d'appel sur la façon dont vous appelez une fonction à partir d'une autre. La convention d'appel est comme si nous décidions si j'écrivais la valeur sur un morceau de mon papier, puis je vous la remettais , ou si vous avez un morceau de papier sur lequel je l'écris, ou si je l'écris sur le mur devant nous deux. C'est aussi un sujet intéressant, mais bien au-delà de la portée de cette réponse déjà longue.)
Appelez par référence
Dans l' appel par référence , les paramètres formels de la fonction sont simplement de nouveaux noms pour les mêmes variables que l'appelant fournit comme arguments.
Pour revenir à notre exemple ci-dessus, cela équivaut à:
Puisque
param
c'est juste un autre nom pourarg
- c'est-à-dire, ils sont la même variable , les changements àparam
sont reflétés dansarg
. C'est la manière fondamentale dont l'appel par référence diffère de l'appel par valeur.Très peu de langues prennent en charge l'appel par référence, mais C ++ peut le faire comme ceci:
Dans ce cas,
param
n'a pas seulement la même valeur quearg
, elle l' est en faitarg
(juste par un nom différent) etbar
peut donc observer qu'ellearg
a été incrémentée.Notez que ce n'est pas ainsi que fonctionnent aujourd'hui Java, JavaScript, C, Objective-C, Python ou presque tout autre langage populaire. Cela signifie que ces langues ne sont pas appelées par référence, elles sont appelées par valeur.
Addendum: appel par partage d'objets
Si ce que vous avez est un appel par valeur , mais que la valeur réelle est un type de référence ou un type de pointeur , la "valeur" elle-même n'est pas très intéressante (par exemple, en C, c'est juste un entier d'une taille spécifique à la plate-forme) - ce qui est ce que cette valeur indique est intéressant .
Si ce que ce type de référence (c'est-à-dire, pointeur) pointe est modifiable, un effet intéressant est possible: vous pouvez modifier la valeur pointée et l'appelant peut observer les modifications de la valeur pointée, même si l'appelant ne peut pas observer modifie le pointeur lui-même.
Pour reprendre l'analogie de l'URL, le fait que je vous ai donné une copie de l'URL vers un site Web n'est pas particulièrement intéressant si la chose qui nous intéresse tous les deux est le site Web, pas l'URL. Le fait que vous gribouilliez votre copie de l'URL n'affecte pas ma copie de l'URL n'est pas une chose qui nous importe (et en fait, dans des langages comme Java et Python, "l'URL", ou la valeur du type de référence, peut ne peut pas être modifié du tout, seule la chose indiquée par lui peut).
Barbara Liskov, lorsqu'elle a inventé le langage de programmation CLU (qui avait cette sémantique), a réalisé que les termes existants "appel par valeur" et "appel par référence" n'étaient pas particulièrement utiles pour décrire la sémantique de ce nouveau langage. Elle a donc inventé un nouveau terme: appel par partage d'objets .
Lorsque je discute de langages qui sont techniquement appelés par valeur, mais où les types courants utilisés sont des types de référence ou de pointeur (c'est-à-dire: presque tous les langages de programmation modernes impératifs, orientés objet ou multi-paradigmes), je trouve que c'est beaucoup moins déroutant pour évitez simplement de parler d' appel par valeur ou d' appel par référence . Restez à appeler par partage d'objets (ou simplement appeler par objet ) et personne ne sera confus. :-)
la source
The first is value versus variable.
The other important pair of concepts to distinguish is parameter versus argument:
Avant de comprendre les 2 termes, vous DEVEZ comprendre ce qui suit. Chaque objet, a 2 choses qui peuvent le distinguer.
Donc si tu dis
employee.name = "John"
sachez qu'il y a 2 choses
name
. Sa valeur qui est"John"
aussi son emplacement dans la mémoire qui est un nombre hexadécimal peut - être comme ceci:0x7fd5d258dd00
.Selon l'architecture du langage ou le type (classe, structure, etc.) de votre objet, vous transférez
"John"
ou0x7fd5d258dd00
Passer
"John"
est connu comme passer par la valeur. Passer0x7fd5d258dd00
est connu comme passer par référence. Toute personne qui pointe vers cet emplacement mémoire aura accès à la valeur de"John"
.Pour en savoir plus, je vous recommande de lire sur le déréférencement d'un pointeur et aussi pourquoi choisir struct (type de valeur) plutôt que classe (type de référence)
la source
Voici un exemple:
la source
y
a déjà été mis à 2 par la ligne précédente. Pourquoi reviendrait-il à 0?Le moyen le plus simple d'obtenir cela est sur un fichier Excel. Disons par exemple que vous avez deux nombres, 5 et 2 dans les cellules A1 et B1 en conséquence, et que vous souhaitez trouver leur somme dans une troisième cellule, disons A2. Vous pouvez le faire de deux manières.
Soit en passant leurs valeurs à la cellule A2 en tapant = 5 + 2 dans cette cellule. Dans ce cas, si les valeurs des cellules A1 ou B1 changent, la somme dans A2 reste la même.
Ou en passant les «références» des cellules A1 et B1 à la cellule A2 en tapant = A1 + B1 . Dans ce cas, si les valeurs des cellules A1 ou B1 changent, la somme dans A2 change également.
la source
Lorsque vous passez par ref, vous passez essentiellement un pointeur sur la variable. Passez par valeur vous passez une copie de la variable. Dans l'utilisation de base, cela signifie normalement que les modifications pass by ref à la variable seront considérées comme la méthode appelante et passeront par la valeur qu'elles ne connaîtront pas.
la source
Le passage par valeur envoie une COPIE des données stockées dans la variable que vous spécifiez, le passage par référence envoie un lien direct vers la variable elle-même. Donc, si vous passez une variable par référence, puis changez la variable à l'intérieur du bloc dans lequel vous l'avez passée, la variable d'origine sera modifiée. Si vous passez simplement par valeur, la variable d'origine ne pourra pas être modifiée par le bloc dans lequel vous l'avez passée, mais vous obtiendrez une copie de ce qu'elle contenait au moment de l'appel.
la source
Passer par valeur - La fonction copie la variable et fonctionne avec une copie (donc elle ne change rien dans la variable d'origine)
Passer par référence - La fonction utilise la variable d'origine, si vous changez la variable dans l'autre fonction, elle change également dans la variable d'origine.
Exemple (copiez et utilisez / essayez vous-même et voyez):
Restez simple, piaule. Les murs de texte peuvent être une mauvaise habitude.
la source
Une différence majeure entre elles est que les variables de type valeur stockent des valeurs, donc la spécification d'une variable de type valeur dans un appel de méthode transmet une copie de la valeur de cette variable à la méthode. Les variables de type référence stockent les références aux objets, donc la spécification d'une variable de type référence comme argument transmet à la méthode une copie de la référence réelle qui fait référence à l'objet. Même si la référence elle-même est passée par valeur, la méthode peut toujours utiliser la référence qu'elle reçoit pour interagir avec - et éventuellement modifier - l'objet d'origine. De même, lors du retour d'informations d'une méthode via une instruction return, la méthode retourne une copie de la valeur stockée dans une variable de type valeur ou une copie de la référence stockée dans une variable de type référence. Lorsqu'une référence est renvoyée, la méthode appelante peut utiliser cette référence pour interagir avec l'objet référencé. Donc,
En c #, pour passer une variable par référence afin que la méthode appelée puisse modifier celle-ci, C # fournit les mots clés ref et out. L'application du mot-clé ref à une déclaration de paramètre vous permet de passer une variable à une méthode par référence - la méthode appelée pourra modifier la variable d'origine dans l'appelant. Le mot clé ref est utilisé pour les variables qui ont déjà été initialisées dans la méthode d'appel. Normalement, lorsqu'un appel de méthode contient une variable non initialisée comme argument, le compilateur génère une erreur. Le fait de précéder un paramètre avec un mot clé out crée un paramètre de sortie. Cela indique au compilateur que l'argument sera transmis à la méthode appelée par référence et que la méthode appelée attribuera une valeur à la variable d'origine dans l'appelant. Si la méthode n'affecte pas de valeur au paramètre de sortie dans tous les chemins d'exécution possibles, le compilateur génère une erreur. Cela empêche également le compilateur de générer un message d'erreur pour une variable non initialisée transmise en tant qu'argument à une méthode. Une méthode ne peut renvoyer qu'une seule valeur à son appelant via une instruction return, mais peut renvoyer de nombreuses valeurs en spécifiant plusieurs paramètres de sortie (ref et / ou out).
voir la discussion c # et les exemples ici lien texte
la source
Exemples:
const &
est généralement le meilleur. Vous n'encourez pas la pénalité de construction et de destruction. Si la référence n'est pas constante, votre interface suggère qu'elle changera les données transmises.la source
En bref, passé par valeur est ce qu'il est et passé par référence est où il est.
Si votre valeur est VAR1 = "Happy Guy!", Vous ne verrez que "Happy Guy!". Si VAR1 devient "Happy Gal!", Vous ne le saurez pas. S'il est transmis par référence et que VAR1 change, vous le ferez.
la source
Si vous ne voulez pas modifier la valeur de la variable d'origine après l'avoir passée dans une fonction, la fonction doit être construite avec un paramètre " passe par valeur ".
La fonction n'aura alors que la valeur mais pas l'adresse de la variable passée. Sans l'adresse de la variable, le code à l'intérieur de la fonction ne peut pas modifier la valeur de la variable comme vu de l'extérieur de la fonction.
Mais si vous souhaitez donner à la fonction la possibilité de modifier la valeur de la variable vue de l'extérieur, vous devez utiliser le passage par référence . Comme la valeur et l'adresse (référence) sont transmises et disponibles dans la fonction.
la source
passer par valeur signifie comment passer de la valeur à une fonction en utilisant des arguments. en passant par valeur, nous copions les données stockées dans la variable que nous spécifions et c'est plus lent que de passer par référence car les données sont copiées. Si nous modifions les données copiées, les données d'origine ne sont pas affectées. En passant par référence ou par adresse, nous envoyons un lien direct vers la variable elle-même. ou en passant le pointeur sur une variable. c'est plus rapide car moins de temps est consommé
la source
Voici un exemple qui montre les différences entre passer par valeur - valeur du pointeur - référence :
La méthode du «passage par référence» présente une limitation importante . Si un paramètre est déclaré transmis par référence (il est donc précédé du signe &), son paramètre réel correspondant doit être une variable .
Un paramètre réel faisant référence au paramètre formel «passé par valeur» peut être une expression en général, il est donc permis d'utiliser non seulement une variable mais aussi un résultat d'invocation de fonction littérale ou même de fonction.
La fonction n'est pas en mesure de placer une valeur dans autre chose qu'une variable. Il ne peut pas affecter une nouvelle valeur à un littéral ni forcer une expression à changer son résultat.
PS: Vous pouvez également vérifier la réponse de Dylan Beattie dans le fil de discussion actuel qui l'explique en termes simples.
la source