web-dev-qa-db-fra.com

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

Quelle est la différence entre

  1. un paramètre passé par référence
  2. un paramètre passé par valeur?

Pourriez-vous me donner des exemples, s'il vous plaît?

527
ritu

Tout d’abord, la distinction "passer par valeur vs passer par référence" telle que définie dans la théorie CS est maintenant obsolète car la technique définie à l'origine comme "passer par référence" est depuis tombée en désuétude et est rarement utilisée à présent.1

Nouvelles langues2 tendent à utiliser une paire de techniques différente (mais similaire) 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 est antérieure à celle-ci).


Maintenant, la définition authentique est:

  • Lorsqu'un paramètre est passé 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 passé 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 choses à 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'assigne, je ' ll changera la variable de l'appelant elle-même, pas par exemple quoi que ce soit qu'il pointe si c'est un pointeur.

    • Ceci est maintenant considéré comme une mauvaise pratique (en tant que dépendance implicite). En tant que tel, , pratiquement toutes les langues plus récentes sont exclusivement, ou presque exclusivement, réputées valeur par passe. La répercussion par référence est maintenant principalement utilisée sous la forme: "arguments de sortie/inout" 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 en gros est une "variable" qui est en quelque sorte "la même" que la variable d'origine. La précision avec laquelle cet effet est obtenu n'est pas pertinente (par exemple, le langage peut également exposer certains détails de la mise en oeuvre - adresses , pointeurs, déréférencement - tout cela n’est pas pertinent; si l’effet net est le suivant, c’est le passage par référence).


Maintenant, dans les langages modernes, les variables ont tendance à être des "types de référence" (un autre concept inventé plus tard que "pass by reference" et inspiré par celui-ci), c'est-à-dire que les données réelles de l'objet sont stockées séparément quelque part (généralement sur le tas), et seules les "références" y figurant sont conservées dans des variables et transmises en tant que paramètres.3

Le passage d'une telle référence relève de la valeur passe-par-valeur car la valeur d'une variable est techniquement la référence elle-même et non l'objet référencé. Cependant, l’effet net sur le programme peut être identique à celui transmis soit par valeur, soit par référence:

  • Si une référence est simplement extraite d'une variable de l'appelant et transmise en tant qu'argument, cela a le même effet que l'étape de référence: 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. Par conséquent, toutes les opérations ultérieures sur cette variable affecteront le contenu pointé.
  • Pour avoir le même effet que la valeur de passage, une copie de l'objet est faite à un moment donné. Les options incluent:
    • L'appelant peut simplement faire une copie privée avant l'appel et donner à l'appelé une référence à cette place.
    • Dans certaines langues, certains types d'objet sont "immuables": toute opération sur ceux-ci qui semble modifier la valeur crée en réalité un objet totalement nouvel sans affecter l'objet d'origine. Ainsi, passer un objet d'un tel type sous forme d'argument a toujours pour effet de passer-à-valeur: une copie pour l'appelé sera faite automatiquement si et quand cela nécessite 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 constater, , cette paire de techniques est presque identique à celle de la définition, mais avec un niveau d'indirection: remplacez simplement "variable" par "objet référencé".

Il n'y a pas de nom convenu pour eux, ce qui conduit à des explications déformées du type "appel par valeur où la valeur est une référence". En 1975, Barbara Liskov a suggéré le terme " partage par appel d'objet " (ou parfois simplement "appel par partage"), bien qu'il ne soit jamais très utilisé. De plus, aucune de ces phrases ne trace un parallèle avec la paire d'origine. Pas étonnant que les anciens termes aient été réutilisés dans l’absence de quelque chose de meilleur, ce qui a semé la confusion.4


NOTE: Pendant longtemps, cette réponse disait:

Dites que je veux 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 modifications. Si vous supprimez l'URL, vous ne faites que 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 aucune modification ultérieure et les modifications que vous apportez (par exemple, le gribouillage sur votre impression) n'apparaîtront pas sur la page d'origine. Si vous détruisez l’impression, vous avez effectivement 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 (il n'a pas être explicite et/ou persistante sont des caractéristiques supplémentaires qui ne font pas partie de la sémantique de référence par référence, comme expliqué ci-dessus). Une analogie plus étroite consisterait à vous donner une copie d'un document plutôt que de vous inviter à travailler sur l'original.


1Sauf si vous programmez en Fortran ou en Visual Basic, ce n'est pas le comportement par défaut et, dans la plupart des langages d'usage courant, un véritable appel par référence n'est même pas possible.

2De nombreuses personnes plus âgées le soutiennent aussi

3Dans plusieurs langues modernes, tous les types sont des types de référence. Cette approche a été initiée par le langage CLU en 1975 et a depuis été adoptée par de nombreux autres langages, notamment Python et Ruby. Et beaucoup plus de langues utilisent une approche hybride, où certains types sont des "types valeur" et d'autres des "types référence", parmi lesquels C #, Java et JavaScript.

4Il n’ya rien de mal à recycler un ancien terme approprié en tant que tel, , mais il est nécessaire de préciser quelle signification est utilisée à chaque fois. Ne pas le faire, c'est ce qui ne cesse de semer la confusion.

1033
Dylan Beattie

C'est un moyen de passer des arguments à des fonctions. Passer par référence signifie que le paramètre des fonctions appelées sera le même que l'argument passé par l'appelant (pas la valeur, mais l'identité - la variable elle-même). Pass par valeur signifie que le paramètre des fonctions appelées sera une copie de l'argument passé par les appelants. La valeur sera la même, mais l'identité - la variable - est différente. Ainsi, les modifications apportées à un paramètre effectué par la fonction appelée dans un cas modifient l'argument passé et, dans l'autre cas, la valeur du paramètre dans la fonction appelée (qui n'est qu'une copie). Rapidement pressé:

  • Java ne supporte que pass 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 pointe vers le même objet et les modifications apportées à cet objet sont visibles dans l'appelant. Comme cela peut être déroutant, ici , dit Jon Skeet à ce sujet.
  • C # prend en charge passe par valeur et passe par référence (mot-clé ref utilisé chez l'appelant et fonction appelée). Jon Skeet a aussi une bonne explication de cela ici .
  • C++ prend en charge passe par valeur et passe par référence (type de paramètre de référence utilisé dans la fonction appelée). Vous trouverez une explication à ce sujet ci-dessous.

Codes

Puisque ma langue est le 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 dans Java ne fera pas 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 mec a bien compris:

http://javadude.com/articles/passbyvalue.htm

134

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

  • Sa valeur.
  • Son adresse.

Donc, si vous dites employee.name = "John"

sais qu'il y a 2 choses à propos de name. Sa valeur, qui est "John", ainsi que son emplacement dans la mémoire, qui est un nombre hexadécimal, peut-être comme ceci: 0x7fd5d258dd00.

En fonction de l'architecture du langage ou du type (classe, structure, etc.) de votre objet, vous pouvez transférer "John" ou 0x7fd5d258dd00

Passer "John" est appelé passer par valeur. Passer 0x7fd5d258dd00 est appelé passer par référence. Toute personne qui pointe vers cet emplacement de mémoire aura accès à la valeur de "John".

Pour plus d'informations à ce sujet, je vous recommande de lire sur déréférencement d'un pointeur et aussi pourquoi choisir struct (type de valeur) sur classe (type de référence)

57
Honey

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
}
53
pyon

Le moyen le plus simple de l'obtenir est d'utiliser 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 voulez trouver leur somme dans une troisième cellule, disons A2. Vous pouvez le faire de deux manières.

  • Soit par 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 par 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 en A2 change également.

27
Than Skourtan

En passant par ref, vous passez essentiellement un pointeur sur la variable. Passer par valeur, vous transmettez une copie de la variable. En utilisation de base, cela signifie normalement que les changements de référence à la variable seront considérés comme étant la méthode appelante et que la valeur de transmission leur sera transmise.

18
Craig

Pass par valeur envoie une copie des données stockées dans la variable que vous spécifiez, passe par référence envoie un lien direct à la variable elle-même. Ainsi, si vous transmettez une variable par référence et que vous modifiez ensuite la variable à l'intérieur du bloc dans lequel vous l'avez transmise, 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.

11
MetaGuru

Pass par valeur - La fonction copie la variable et fonctionne avec une copie (pour ne rien changer à la variable d'origine)

Passer par référence - La fonction utilise la variable d'origine. Si vous modifiez la variable dans l'autre fonction, elle change également dans la variable d'origine.

Exemple (copier et utiliser/essayer ceci vous-même et voir):

#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;
}

Gardez les choses simples, peeps. Les murs de texte peuvent être une mauvaise habitude.

6
user326964

Une différence majeure entre elles réside dans le fait que les variables de type valeur stockent des valeurs. Par conséquent, 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 des références à des objets. Par conséquent, la spécification d'une variable de type référence en tant qu'argument transmet à la méthode une copie de la référence réelle faisant 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 renvoi d'informations d'une méthode via une instruction return, la méthode renvoie 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é. Ainsi, en effet, les objets sont toujours passés par référence.

En c #, pour transmettre une variable par référence afin que la méthode appelée puisse modifier celle de la variable, C # fournit les mots-clés ref et out. L'application du mot-clé ref à une déclaration de paramètre vous permet de transmettre une variable à une méthode par référence. La méthode appelée peut 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 en tant qu'argument, le compilateur génère une erreur. Le fait de faire précéder un paramètre avec le mot clé out crée un paramètre de sortie. Cela indique au compilateur que l'argument sera passé à la méthode appelée par référence et que la méthode appelée affectera une valeur à la variable d'origine dans l'appelant. Si la méthode n'attribue pas de valeur au paramètre de sortie dans chaque chemin d'exécution possible, 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 valeur à son appelant via une instruction return, mais elle peut renvoyer de nombreuses valeurs en spécifiant plusieurs paramètres de sortie (ref et/ou out).

voir c # discussion et exemples ici texte du lien

5
Tina Endresen

Exemples:

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

const & est généralement le meilleur. Vous ne encourez pas la peine de construction et de destruction. Si la référence n'est pas const, votre interface suggère que les données transmises seront modifiées.

3
DEADFACE

En bref, Passed by value est WHAT it is et transmis par référence WHERE it.

Si votre valeur est VAR1 = "Happy Guy!", Vous ne verrez que "Happy Guy!". Si VAR1 devient "Happy Gal!", Vous ne le saurez pas. Si c'est passé par référence et que VAR1 change, vous le ferez.

2
Monster

passer par valeur signifie comment passer de la valeur à une fonction en utilisant des arguments. passe par valeur, nous copions les données stockées dans la variable que nous spécifions; elle est plus lente que de passer par référence car les données sont copiées. Si nous apportons des modifications aux données copiées, les données d'origine ne sont pas affectées. nd in pass by refernce ou pass by address, nous envoyons un lien direct vers la variable elle-même. ou en passant le pointeur à une variable. il est plus rapide bcse moins de temps est consommé

1
abhinisha thakur

Si vous ne souhaitez pas modifier la valeur de la variable d'origine après l'avoir passée dans une fonction, celle-ci doit être construite avec un paramètre "transmission par valeur".

La fonction aura alors UNIQUEMENT la valeur mais pas l'adresse de la variable transmise. Sans l'adresse de la variable, le code à l'intérieur de la fonction ne peut pas changer la valeur de la variable vue de l'extérieur de la fonction.

Mais si vous voulez donner à la fonction la possibilité de changer la valeur de la variable vue de l'extérieur, vous devez utiliser pass par référence. Comme la valeur et l'adresse (référence) sont transmises et disponibles dans la fonction.

1
Stanley

Voici un exemple qui montre les différences entre passage par valeur - pointeur valeur - 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 de "passage par référence" a ne limitation importante. Si un paramètre est déclaré en tant que passé par référence (il est donc précédé du signe &), son le paramètre réel doit être une variable.

Un paramètre réel faisant référence à un paramètre formel "passé par valeur" peut être ne expression en général, de sorte qu'il est permis d'utiliser non seulement une variable mais également un résultat littéral ou même un appel de fonction.

La fonction n'est pas capable 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 à modifier 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.

0
BugShotGG