52 votes

Passer par référence ou passer par valeur ?

Lorsque vous apprenez un nouveau langage de programmation, l'un des obstacles possibles que vous pouvez rencontrer est la question de savoir si le langage est, par défaut, pass-by-value ou pass-by-reference .

Voici donc ma question à vous tous, dans votre langue préférée, comment est-ce que cela se fait réellement ? Et quels sont les pièges possibles ?

Votre langue préférée peut, bien sûr, être n'importe quelle langue avec laquelle vous avez déjà joué : populaire , obscurité , ésotérique , nouveau , vieux ...

0 votes

Il y a déjà une réponse ici qui explique la situation en PHP5.

4 votes

Wow - cette question a un lien vers le serveur bêta. Je pense que vous devriez corriger le lien en bêta.

31voto

Sven Points 7277

Voici ma propre contribution pour le Langage de programmation Java .

d'abord un peu de code :

public void swap(int x, int y)
{
  int tmp = x;
  x = y;
  y = tmp;
}

L'appel de cette méthode donnera le résultat suivant :

int pi = 3;
int everything = 42;

swap(pi, everything);

System.out.println("pi: " + pi);
System.out.println("everything: " + everything);

"Output:
pi: 3
everything: 42"

même en utilisant des objets "réels", le résultat sera similaire :

public class MyObj {
    private String msg;
    private int number;

    //getters and setters
    public String getMsg() {
        return this.msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public int getNumber() {
        return this.number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    //constructor
    public MyObj(String msg, int number) {
        setMsg(msg);
        setNumber(number);
    }
}

public static void swap(MyObj x, MyObj y)
{
    MyObj tmp = x;
    x = y;
    y = tmp;
}

public static void main(String args[]) {
    MyObj x = new MyObj("Hello world", 1);
    MyObj y = new MyObj("Goodbye Cruel World", -1); 

    swap(x, y);

    System.out.println(x.getMsg() + " -- "+  x.getNumber());
    System.out.println(y.getMsg() + " -- "+  y.getNumber());
}

"Output:
Hello world -- 1
Goodbye Cruel World -- -1"

il est donc clair que Java passe ses paramètres par valeur comme la valeur de pi y tout et le Objets MyObj ne sont pas échangés. soyez conscient que "par valeur" est le seul moyen en java pour passer des paramètres à une méthode. (par exemple, un langage comme c++ permet au développeur de passer un paramètre par référence en utilisant ' & après le type du paramètre)

maintenant le partie délicate ou du moins la partie qui va dérouter la plupart des nouveaux développeurs java : (emprunté à javaworld )
Auteur original : Tony Sintes

public void tricky(Point arg1, Point arg2)
{
    arg1.x = 100;
    arg1.y = 100;
    Point temp = arg1;
    arg1 = arg2;
    arg2 = temp;
}
public static void main(String [] args)
{
    Point pnt1 = new Point(0,0);
    Point pnt2 = new Point(0,0);
    System.out.println("X: " + pnt1.x + " Y: " +pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
    System.out.println(" ");
    tricky(pnt1,pnt2);
    System.out.println("X: " + pnt1.x + " Y:" + pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);  
}

"Output
X: 0 Y: 0
X: 0 Y: 0
X: 100 Y: 100
X: 0 Y: 0"

délicat modifie avec succès la valeur de pnt1 ! Cela impliquerait que les objets sont passés par référence, ce qui n'est pas le cas ! Une déclaration correcte serait : le site Références d'objets sont transmises par valeur.

plus de Tony Sintes :

La méthode modifie avec succès le la valeur de pnt1, même si elle est passé par valeur ; cependant, un échange de pnt1 et pnt2 échoue ! C'est la principale source principale de confusion. Dans la méthode main() pnt1 et pnt2 ne sont rien d'autre que des références que des références d'objets. Lorsque vous passez pnt1 et pnt2 à la méthode tricky(), Java transmet les références par valeur comme tout autre paramètre. Ceci Cela signifie que les références transmises à la sont en fait des copies des références références originales. La figure 1 ci-dessous montre deux références pointant vers le même objet après que Java ait transmis un objet à une méthode.

figure 1
(source : <a href="http://www.javaworld.com/javaworld/javaqa/2000-05/images/03-qa-0512-pass2b.gif" rel="nofollow noreferrer">javaworld.com </a>)

Conclusion ou une longue histoire courte :

  • Java lui transmet des paramètres par valeur
  • "par valeur" est le seul moyen en java pour passer un paramètre à une méthode
  • en utilisant des méthodes de l'objet donné comme paramètre modifiera l'objet comme les références pointent vers les objets originaux. (si cette méthode elle-même modifie certaines valeurs)

des liens utiles :

20voto

Sven Points 7277

Voici un autre article pour le langage de programmation c#

c# passe ses arguments par valeur (par défaut)

private void swap(string a, string b) {
  string tmp = a;
  a = b;
  b = tmp;
}

L'appel à cette version de swap n'aura donc aucun résultat :

string x = "foo";
string y = "bar";
swap(x, y);

"output: 
x: foo
y: bar"

cependant, contrairement au java c# fait donner au développeur la possibilité de passer des paramètres par référence Pour ce faire, on utilise le mot clé "ref" devant le type du paramètre :

private void swap(ref string a, ref string b) {
  string tmp = a;
  a = b;
  b = tmp;
} 

cet échange sera modifier la valeur du paramètre référencé :

string x = "foo";
string y = "bar";
swap(x, y);

"output: 
x: bar
y: foo"

Le c# dispose également d'un mot clé de sortie et la différence entre ref et out est subtile. de msdn :

L'appelant d'une méthode qui prend un paramètre de sortie n'est pas tenu de à la variable passée en tant que paramètre out avant l'appel ; Cependant, le destinataire de l'appel est nécessaire pour affecter au paramètre out avant de retour.

y

En revanche paramètres de référence sont considéré comme initialement attribué par le l'appelant. En tant que tel, l'appelé est pas nécessaire d'affecter à la réf avant de l'utiliser. Les paramètres Ref sont passés à la fois dans et hors d'une méthode.

un petit piège est, comme en java, que les objets transmis par valeur peuvent toujours être modifiés à l'aide de leurs méthodes internes.

conclusion :

  • c# passe ses paramètres, par défaut, par valeur
  • mais si nécessaire, des paramètres peuvent également être passés par référence en utilisant le mot clé ref
  • des méthodes internes à partir d'un paramètre passé par valeur modifiera l'objet (si cette méthode modifie elle-même certaines valeurs)

des liens utiles :

19voto

yukondude Points 8756

Python utilise la méthode pass-by-value, mais comme toutes ces valeurs sont des références d'objets, l'effet net s'apparente à la méthode pass-by-reference. Cependant, les programmeurs Python pensent davantage à savoir si un type d'objet est mutable o immuable . Les objets mutables peuvent être modifiés sur place (par exemple, les dictionnaires, les listes, les objets définis par l'utilisateur), alors que les objets immuables ne le peuvent pas (par exemple, les entiers, les chaînes de caractères, les tuples).

L'exemple suivant montre une fonction à laquelle on passe deux arguments, une chaîne immuable et une liste mutable.

>>> def do_something(a, b):
...     a = "Red"
...     b.append("Blue")
... 
>>> a = "Yellow"
>>> b = ["Black", "Burgundy"]
>>> do_something(a, b)
>>> print a, b
Yellow ['Black', 'Burgundy', 'Blue']

La ligne a = "Red" crée simplement un nom local, a pour la valeur de la chaîne "Red" et n'a pas d'effet sur l'argument passé (qui est maintenant caché, car a doit se référer au nom local à partir de ce moment-là). L'assignation n'est pas une opération in-place, que l'argument soit mutable ou immuable.

Le site b est une référence à un objet liste mutable, et le paramètre .append() effectue une extension in situ de la liste, en ajoutant le nouvel élément "Blue" valeur de la chaîne.

(Les objets de type chaîne étant immuables, ils ne disposent d'aucune méthode prenant en charge les modifications in situ).

Une fois que la fonction revient, la réaffectation de la fonction a n'a eu aucun effet, tandis que l'extension de b montre clairement la sémantique d'appel du style pass-by-reference.

Comme mentionné précédemment, même si l'argument pour a est un type mutable, la réaffectation au sein de la fonction n'est pas une opération in-place, et il n'y aura donc aucun changement de la valeur de l'argument passé :

>>> a = ["Purple", "Violet"]
>>> do_something(a, b)
>>> print a, b
['Purple', 'Violet'] ['Black', 'Burgundy', 'Blue', 'Blue']

Si vous ne vouliez pas que votre liste soit modifiée par la fonction appelée, vous utiliseriez plutôt le type tuple immuable (identifié par les parenthèses dans la forme littérale, plutôt que par les crochets), qui ne prend pas en charge la fonction in-place .append() méthode :

>>> a = "Yellow"
>>> b = ("Black", "Burgundy")
>>> do_something(a, b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in do_something
AttributeError: 'tuple' object has no attribute 'append'

2 votes

D'après ce que j'ai lu lors d'un rapide survol des discussions sur le passage d'arguments en Python sur le web, la plupart des utilisateurs de Python ne savent pas ce que signifie passer par référence. Python est définitivement pass by value. L'immuabilité des valeurs est une question distincte. Et puis il y a les gens qui s'embrouillent avec les liaisons de dictionnaire, et qui ne comprennent pas qu'un symbole lié à une référence à une valeur dans un dictionnaire est la même chose qu'une variable contenant une référence à une valeur. Le passage par référence est le cas où vous passez une référence à la variable, et non à la valeur ; ou dans le langage des symboles, où vous passez une liaison de nom mutable.

7voto

LeoNerd Points 3940

Comme je n'ai pas encore vu de réponse en Perl, j'ai pensé en écrire une.

Sous le capot, Perl fonctionne efficacement en tant que pass-by-reference. Les variables en tant qu'arguments d'appel de fonction sont passées par référence, les constantes sont passées comme des valeurs en lecture seule, et les résultats des expressions sont passés comme des temporaires. Les idiomes habituels pour construire des listes d'arguments par assignation de liste à partir de @_ ou par shift ont tendance à cacher cela à l'utilisateur, donnant l'apparence d'un passage par valeur :

sub incr {
  my ( $x ) = @_;
  $x++;
}

my $value = 1;
incr($value);
say "Value is now $value";

Ceci imprimera Value is now 1 parce que le $x++ a incrémenté la variable lexicale déclarée dans l'élément incr() plutôt que la variable transmise. Ce style pass-by-value est généralement ce qui est souhaité la plupart du temps, car les fonctions qui modifient leurs arguments sont rares en Perl, et ce style doit être évité.

Toutefois, si, pour une raison ou une autre, ce comportement est spécifiquement souhaité, il est possible de l'obtenir en opérant directement sur les éléments de la base de données de l'UE. @_ car ils seront des alias pour les variables passées dans la fonction.

sub incr {
  $_[0]++;
}

my $value = 1;
incr($value);
say "Value is now $value";

Cette fois, il imprimera Value is now 2 parce que le $_[0]++ a incrémenté la valeur actuelle de $value variable. La façon dont cela fonctionne est que sous le capot @_ n'est pas un tableau réel comme la plupart des autres tableaux (tels qu'ils seraient obtenus par my @array ), mais au lieu de cela, ses éléments sont construits directement à partir des arguments passés à un appel de fonction. Cela vous permet de construire une sémantique pass-by-reference si cela s'avère nécessaire. Les arguments d'appel de fonction qui sont des variables simples sont insérés tels quels dans ce tableau, et les constantes ou les résultats d'expressions plus complexes sont insérés en tant que temporaires en lecture seule.

Il est cependant extrêmement rare de le faire en pratique, car Perl supporte les valeurs de référence, c'est-à-dire les valeurs qui font référence à d'autres variables. Normalement, il est beaucoup plus clair de construire une fonction qui a un effet secondaire évident sur une variable, en passant une référence à cette variable. C'est une indication claire pour le lecteur à l'emplacement de l'appel, que la sémantique pass-by-reference est en vigueur.

sub incr_ref {
  my ( $ref ) = @_;
  $$ref++;
}

my $value = 1;
incr(\$value);
say "Value is now $value";

Ici, le \ produit une référence de la même manière que l'opérateur & opérateur d'adresse en C.

6voto

Karl Seguin Points 10566

Il y a un bonne explication ici pour .NET.

Beaucoup de gens s'étonnent que les objets de référence soient en fait transmis par valeur (tant en C# qu'en Java). Il s'agit d'une copie d'une adresse de pile. Cela empêche une méthode de changer l'endroit où l'objet pointe réellement, mais permet toujours à une méthode de changer les valeurs de l'objet. En C#, il est possible de transmettre une référence par référence, ce qui signifie que vous pouvez modifier l'emplacement d'un objet réel.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X