Quelle est la différence entre passer par référence et passer par valeur?

Réponses:

1080

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 .

    • Ceci est maintenant considéré comme une mauvaise pratique (comme une dépendance implicite). En tant que tel, pratiquement toutes les langues plus récentes sont exclusivement ou presque exclusivement pass-by-value. Le passage par référence est désormais principalement utilisé sous la forme d '"arguments de sortie / entrée" dans les langues où une fonction ne peut pas renvoyer plus d'une valeur.
  • 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:

  • Si une référence est simplement extraite de la variable d'un appelant et passée en argument, cela a le même effet que pass-by-reference: si l'objet référencé est muté dans l'appelé, l'appelant verra le changement.
    • Cependant, si une variable contenant cette référence est réaffectée, elle cessera de pointer vers cet objet, donc toute autre opération sur cette variable affectera à la place ce qu'elle pointe maintenant.
  • Pour avoir le même effet que la valeur de passage, une copie de l'objet est effectuée à un moment donné. Les options comprennent:
    • L'appelant peut simplement faire une copie privée avant l'appel et donner à l'appelé une référence à la place.
    • Dans certains langages, certains types d'objets sont "immuables": toute opération sur ceux-ci qui semble modifier la valeur crée en fait un objet complètement nouveau sans affecter celui d'origine. Ainsi, le passage d'un objet d'un tel type en tant qu'argument a toujours pour effet de passer par valeur: une copie pour l'appelé sera effectuée automatiquement si et quand il a besoin d'une modification, et l'objet de l'appelant ne sera jamais affecté.
      • Dans les langages fonctionnels, tous les objets sont immuables.

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:

Supposons que je souhaite partager une page Web avec vous. Si je vous dis l'URL, je passe par référence. Vous pouvez utiliser cette URL pour voir la même page Web que je peux voir. Si cette page est modifiée, nous voyons tous les deux les changements. Si vous supprimez l'URL, tout ce que vous faites est de détruire votre référence à cette page - vous ne supprimez pas la page elle-même.

Si j'imprime la page et vous donne l'impression, je passe par valeur. Votre page est une copie déconnectée de l'original. Vous ne verrez pas de modifications ultérieures et toutes les modifications que vous apportez (par exemple, griffonner sur votre impression) n'apparaîtront pas sur la page d'origine. Si vous détruisez l'impression, vous avez en fait détruit votre copie de l'objet - mais la page Web d'origine reste intacte.

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.

ivan_pozdeev
la source
Personnellement, j'utiliserais les termes "nouveau" ou "indirect" passe-par-valeur / passe-par-référence pour les nouvelles techniques.
ivan_pozdeev
La définition «authentique» que vous fournissez n'est pas la définition donnée dans presque tous les cours de programmation d'introduction. Google ce qui est passé par référence et vous n'obtiendrez pas cette réponse. La définition authentique que vous fournissez est une mauvaise utilisation du mot référence, car lorsque vous suivez cette définition, vous utilisez un alias et non une référence: vous avez deux variables qui sont en fait la même variable, c'est-à-dire un alias et non une référence. Votre définition authentique provoque une confusion de masse sans raison. Dites simplement que passer par référence signifie passer l'adresse. Cela a du sens et éviterait cette confusion inutile.
YungGun
@YungGun 1) Veuillez fournir un lien vers une "définition donnée dans presque tous les cours de programmation d'introduction". Notez également que cela vise à être clair dans les réalités d'aujourd'hui, pas dans les réalités d'il y a une décennie ou trois quand un cours de CS a été écrit. 2) "Adresse" ne peut pas être utilisée dans la définition car elle fait délibérément abstraction des implémentations possibles. Par exemple, certaines langues (Fortran) n'ont pas de pointeurs; ils diffèrent également en ce qu'ils exposent l'adresse brute à l'utilisateur (VB ne le fait pas); il ne doit pas non plus être une adresse de mémoire brute, tout ce qui permettrait de se lier à la variable ferait l'affaire.
ivan_pozdeev
@ivan_podeev aucun lien désolé. Je dis "presque tous les cours d'introduction" parce que personnellement, je suis allé à l'université et j'ai aussi pris des bootcamps de programmation qui m'ont appris cela. Ces cours étaient modernes (il y a moins de 5 ans). Une "adresse brute" est synonyme d'un "pointeur" ... Vous pouvez être techniquement correct (selon un lien choisi par les cerises) mais le langage que vous utilisez est peu pratique et déroutant pour la plupart des programmeurs. Si vous voulez avoir toutes mes pensées à ce sujet, j'ai écrit un article de blog de 3500 mots: medium.com/@isaaccway228/…
YungGun
@YungGun "trop ​​long, n'a pas lu". Un coup d'œil montre exactement les confusions décrites dans la réponse. Le passage par référence est une technique abstraite indépendante de la mise en œuvre. Peu importe ce qui se passe exactement sous le capot, il importe quel est l'effet sur le programme.
ivan_pozdeev
150

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:

  • Java ne prend en charge que le passage par valeur. Copie toujours les arguments, même si lors de la copie d'une référence à un objet, le paramètre de la fonction appelée pointera vers le même objet et les modifications apportées à cet objet seront visibles dans l'appelant. Comme cela peut être déroutant, voici ce que Jon Skeet a à dire à ce sujet.
  • C # prend en charge le passage par valeur et le passage par référence (mot clé refutilisé lors de l'appelant et de la fonction appelée). Jon Skeet a également une belle explication à ce sujet ici .
  • C ++ prend en charge le passage par valeur et le passage par référence (type de paramètre de référence utilisé à la fonction appelée). Vous en trouverez une explication ci-dessous.

Codes

Puisque mon langage est C ++, je vais l'utiliser ici

// passes a pointer (called reference in java) to an integer
void call_by_value(int *p) { // :1
    p = NULL;
}

// passes an integer
void call_by_value(int p) { // :2
    p = 42;
}

// passes an integer by reference
void call_by_reference(int & p) { // :3
    p = 42;
}

// this is the java style of passing references. NULL is called "null" there.
void call_by_value_special(int *p) { // :4
    *p = 10; // changes what p points to ("what p references" in java)
    // only changes the value of the parameter, but *not* of 
    // the argument passed by the caller. thus it's pass-by-value:
    p = NULL;
}

int main() {
    int value = 10;
    int * pointer = &value;

    call_by_value(pointer); // :1
    assert(pointer == &value); // pointer was copied

    call_by_value(value); // :2
    assert(value == 10); // value was copied

    call_by_reference(value); // :3
    assert(value == 42); // value was passed by reference

    call_by_value_special(pointer); // :4
    // pointer was copied but what pointer references was changed.
    assert(value == 10 && pointer == &value);
}

Et un exemple en Java ne fera pas de mal:

class Example {
    int value = 0;

    // similar to :4 case in the c++ example
    static void accept_reference(Example e) { // :1
        e.value++; // will change the referenced object
        e = null; // will only change the parameter
    }

    // similar to the :2 case in the c++ example
    static void accept_primitive(int v) { // :2
        v++; // will only change the parameter
    }        

    public static void main(String... args) {
        int value = 0;
        Example ref = new Example(); // reference

        // note what we pass is the reference, not the object. we can't 
        // pass objects. The reference is copied (pass-by-value).
        accept_reference(ref); // :1
        assert ref != null && ref.value == 1;

        // the primitive int variable is copied
        accept_primitive(value); // :2
        assert value == 0;
    }
}

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

Johannes Schaub - litb
la source
9
pourquoi le downvote? si quelque chose ne va pas ou conduit à des malentendus, veuillez laisser un commentaire.
Johannes Schaub - litb
1
Ce n'était pas mon vote, mais sur un point mineur (vous savez, le genre repris dans les débats présidentiels), je dirais que c'est plus une "tactique" qu'une "stratégie".
harpo
28
+1 pour la complétude. Ne vous inquiétez pas des votes négatifs - les gens le font pour des raisons étranges. Dans une question d'opinion sur les calculatrices, tout le monde a été rétrogradé par un gars qui ne pensait pas que les programmeurs devraient utiliser des calculatrices! Quoi qu'il en soit, je pensais que votre réponse était très bonne.
Mark Brittingham
1
Les liens vers les explications de Skeet sont rompus.
Programmeur orienté vers l'argent
Les liens vers les explications de Skeet sont toujours rompus.
Rokit
85

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:

  • appeler par valeur signifie que vous passez des valeurs comme arguments de fonction
  • l'appel par référence signifie que vous passez des variables comme arguments de fonction

En termes métaphoriques:

  • Appel par valeur est l'endroit où j'écris quelque chose sur un morceau de papier et je te le donne . C'est peut-être une URL, c'est peut-être une copie complète de War and Peace. Peu importe ce que c'est, c'est sur un morceau de papier que je vous ai donné, et maintenant c'est effectivement votre morceau de papier . Vous êtes maintenant libre de griffonner sur ce morceau de papier, ou d'utiliser ce morceau de papier pour trouver quelque chose ailleurs et jouer avec lui, peu importe.
  • L'appel par référence, c'est quand je vous donne mon cahier qui contient quelque chose d'écrit . Vous pouvez griffonner dans mon cahier (peut-être que je veux que vous le fassiez, peut-être que je ne le fais pas), et ensuite je garde mon cahier, avec tous les gribouillis que vous y avez mis. De plus, si ce que vous ou moi avons écrit contient des informations sur la façon de trouver quelque chose ailleurs, vous ou moi pouvons y aller et jouer avec ces informations.

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 les classtypes), 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 exemple int*).


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:

void foo(int param) {  // line 1
  param += 1;
}

Et voici un exemple d'appel de cette fonction:

void bar() {
  int arg = 1;  // line 2
  foo(arg);     // line 3
}

En utilisant cet exemple, je veux définir quelques bits importants de terminologie:

  • fooest 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)
  • paramest un paramètre formel de foo, également déclaré sur la ligne 1
  • argest une variable , spécifiquement une variable locale de la fonction bar, déclarée et initialisée sur la ligne 2
  • argest également un argument pour une invocation spécifique de la fooligne 3

Il y a deux ensembles de concepts très importants à distinguer ici. Le premier est la valeur par rapport à la variable :

  • Une valeur est le résultat de l'évaluation d'une expression dans la langue. Par exemple, dans la barfonction ci-dessus, après la ligne int arg = 1;, l'expression arga la valeur 1 .
  • Une variable est un conteneur de valeurs . Une variable peut être mutable (c'est la valeur par défaut dans la plupart des langages de type C), en lecture seule (par exemple déclarée en utilisant Java finalou 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 :

  • Un paramètre (également appelé paramètre formel ) est une variable qui doit être fournie par l'appelant lors de l'appel d'une fonction.
  • Un argument est une valeur fournie par l'appelant d'une fonction pour satisfaire un paramètre formel spécifique de cette fonction

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:

int arg = 1;
int another_variable = arg;

Ici arget another_variablesont des variables complètement indépendantes - leurs valeurs peuvent changer indépendamment les unes des autres. Cependant, au point où another_variableest déclaré, il est initialisé pour contenir la même valeur que celle qui argest - qui est 1.

Puisqu'il s'agit de variables indépendantes, les modifications à another_variablene pas affecter arg:

int arg = 1;
int another_variable = arg;
another_variable = 2;

assert arg == 1; // true
assert another_variable == 2; // true

C'est exactement la même chose que la relation entre arget paramdans notre exemple ci-dessus, que je vais répéter ici pour la symétrie:

void foo(int param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

C'est exactement comme si nous avions écrit le code de cette façon:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here

C'est-à-dire que la caractéristique déterminante de ce que signifie l' appel par valeur est que l'appelé ( foodans ce cas) reçoit des valeurs comme arguments, mais a ses propres variables distinctes pour ces valeurs des variables de l'appelant ( bardans ce cas).

Pour en revenir à ma métaphore ci-dessus, si je suis baret vous foo, quand je vous appelle, je vous donne un morceau de papier avec une valeur écrite dessus. Vous appelez ce morceau de papier param. Cette valeur est une copie de la valeur que j'ai écrite dans mon cahier (mes variables locales), dans une variable que j'appelle arg.

(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 à:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
// aha! I note that "param" is just another name for "arg"
arg /* param */ += 1;
// exiting function "foo" here
// exiting function "bar" here

Puisque paramc'est juste un autre nom pour arg- c'est-à-dire, ils sont la même variable , les changements à paramsont reflétés dans arg. 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:

void foo(int& param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

Dans ce cas, paramn'a pas seulement la même valeur que arg, elle l' est en faitarg (juste par un nom différent) et barpeut donc observer qu'elle arga é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. :-)

Daniel Pryden
la source
Mieux expliqué: Il y a deux ensembles de concepts très importants à distinguer ici. The first is value versus variable. The other important pair of concepts to distinguish is parameter versus argument:
SK Venkat
2
Excellente réponse. Je pense que j'ajouterais qu'aucun nouveau stockage n'a besoin d'être créé en passant par référence. Le nom du paramètre fait référence au stockage d'origine (mémoire) .Merci
drlolly
1
Meilleure réponse OMI
Rafael Eyng
59

Avant de comprendre les 2 termes, vous DEVEZ comprendre ce qui suit. Chaque objet, a 2 choses qui peuvent le distinguer.

  • Sa valeur.
  • Son adresse.

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. Passer 0x7fd5d258dd00est 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)

Mon chéri
la source
3
C'est ce que je cherchais, en fait il faut chercher le concept et pas seulement l'explication, bravo bro.
Haisum Usman,
Java est toujours transmis par valeur. Passer des références à des objets en Java est considéré comme passant par valeur. Cela contredit votre déclaration "Passer 0x7fd5d258dd00 est connu comme passer par référence.".
rainet chetan
53

Voici un exemple:

#include <iostream>

void by_val(int arg) { arg += 2; }
void by_ref(int&arg) { arg += 2; }

int main()
{
    int x = 0;
    by_val(x); std::cout << x << std::endl;  // prints 0
    by_ref(x); std::cout << x << std::endl;  // prints 2

    int y = 0;
    by_ref(y); std::cout << y << std::endl;  // prints 2
    by_val(y); std::cout << y << std::endl;  // prints 2
}
pyon
la source
1
Je pense qu'il y a un problème car la dernière ligne devrait imprimer 0 au lieu de 2. Veuillez me dire si je manque quelque chose.
Taimoor Changaiz
@TaimoorChangaiz; Quelle "dernière ligne"? Au fait, si vous pouvez utiliser IRC, veuillez venir à la programmation ## sur Freenode. Il serait beaucoup plus facile d'expliquer les choses là-bas. Mon pseudo c'est "pyon".
pyon
1
@ EduardoLeón by_val (y); std :: cout << y << std :: endl; // tirages 2
Taimoor Changaiz
5
@TaimoorChangaiz: Pourquoi n'imprimerait-il pas 2? ya déjà été mis à 2 par la ligne précédente. Pourquoi reviendrait-il à 0?
pyon
@ EduardoLeón ma mauvaise. Oui, tu as raison. Merci pour la correction
Taimoor Changaiz
28

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.

Than Skourtan
la source
C'est le plus simple et le meilleur exemple parmi toutes les autres réponses.
Amit Ray
18

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.

Craig
la source
12

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.

MetaGuru
la source
7

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):

#include <iostream>

using namespace std;

void funct1(int a){ //pass-by-value
    a = 6; //now "a" is 6 only in funct1, but not in main or anywhere else
}
void funct2(int &a){ //pass-by-reference
    a = 7; //now "a" is 7 both in funct2, main and everywhere else it'll be used
}

int main()
{
    int a = 5;

    funct1(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 5
    funct2(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 7

    return 0;
}

Restez simple, piaule. Les murs de texte peuvent être une mauvaise habitude.

user326964
la source
C'est très utile pour comprendre si la valeur du paramètre a été modifiée ou non, merci!
Kevin Zhao
5

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

Tina Endresen
la source
3

Exemples:

class Dog 
{ 
public:
    barkAt( const std::string& pOtherDog ); // const reference
    barkAt( std::string pOtherDog ); // value
};

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.

Déduplicateur
la source
2

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.

Monstre
la source
2

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.

Stanley
la source
1

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é

abhinisha thakur
la source
0

Voici un exemple qui montre les différences entre passer par valeur - valeur du pointeur - référence :

void swap_by_value(int a, int b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}   
void swap_by_pointer(int *a, int *b){
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}    
void swap_by_reference(int &a, int &b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}

int main(void){
    int arg1 = 1, arg2 = 2;

    swap_by_value(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 1 2

    swap_by_pointer(&arg1, &arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1

    arg1 = 1;                               //reset values
    arg2 = 2;
    swap_by_reference(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1
}

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.

BugShotGG
la source
Vous dites "si un paramètre est déclaré [comme référence], son paramètre réel correspondant doit être une variable", mais ce n'est pas vrai en général. Si une référence est liée à un temporaire (comme la valeur de retour d'une fonction), sa durée de vie est étendue pour correspondre à la référence. Voir ici pour plus de détails.
Chris Hunt