language agnostic - Passer par référence ou passer par valeur?

Translate

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

Alors voici ma question à vous tous, dans votre langue préférée,Commentest-ce vraiment fait? Et quels sont lesécueils possibles?

Votre langue préférée peut, bien sûr, être tout ce avec quoi vous avez déjà joué:populaire, obscur, ésotérique, Nouveau, vieux...

This question and all comments follow the "Attribution Required."

Toutes les réponses

Translate

Voici ma propre contribution pour leLangage 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 entraînera ceci:

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 l'utilisation d'objets `` réels '' affichera un résultat 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ètrespar valeur, comme valeur depiettoutet leObjets MyObjne sont pas échangés. sachez que "par valeur" est leseul moyenen java pour passer des paramètres à une méthode. (par exemple, un langage comme le c ++ permet au développeur de passer un paramètre par référence en utilisant '&'après le type du paramètre)

maintenant lepartie délicate, ou du moins la partie qui déroutera 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"

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

plus de Tony Sintes:

La méthode modifie avec succès la valeur de pnt1, même si elle est passée par valeur; cependant, un échange de pnt1 et pnt2 échoue! C'est la principale source de confusion. Dans la méthode main (), pnt1 et pnt2 ne sont rien de plus 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. Cela signifie que les références passées à la méthode sont en fait des copies des références d'origine. La figure 1 ci-dessous montre deux références pointant vers le même objet une fois que Java a transmis un objet à une méthode.

figure 1
(la source:javaworld.com)

Conclusionou une histoire courte:

  • Java lui transmet les paramètrespar valeur
  • "par valeur"est leseul moyenen java pour passer un paramètre à une méthode
  • en utilisantméthodes de l'objetdonné en paramètreva changerl'objet comme les références pointent vers les objets d'origine. (si cette méthode elle-même modifie certaines valeurs)

Liens utiles:

La source
Translate

Voici un autre article pour lelangage de programmation c #

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

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

l'appel de 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 à javac #Est-ce quedonner au développeur la possibilité de passer des paramètrespar référence, cela se fait en utilisant le mot-clé 'ref' avant le type du paramètre:

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

cet échangevolontémodifiez la valeur du paramètre référencé:

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

"output: 
x: bar
y: foo"

c # a également unout mot-clé, et la différence entre ref et out est subtile.depuis msdn:

L'appelant d'une méthode qui prend unparamètre outn'est pas obligé d'affecter la variable transmise comme paramètre de sortie avant l'appel; cependant, l'appelé estrequis pour affecter le paramètre out avant de retourner.

et

En revancheparamètres refsontconsidéré initialement attribuépar l'appelé. En tant que tel, l'appelé estpas nécessaire d'attribuer à la réfparamètre avant utilisation. Les paramètres Ref sont passés à la fois dans et hors d'une méthode.

un petit écueil est, comme en java, queles objets passés par valeur peuvent toujours être modifiés en utilisant leurs méthodes internes

conclusion:

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

Liens utiles:

La source
Translate

Pythonutilise le passage par valeur, mais comme toutes ces valeurs sont des références d'objet, l'effet net s'apparente au passage par référence. Cependant, les programmeurs Python se demandent si un type d'objet estmutableouimmuable. 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 peuvent pas (par exemple, les entiers, les chaînes, les tuples).

L'exemple suivant montre une fonction qui reçoit 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 lignea = "Red"crée simplement un nom local,a, pour la valeur de chaîne"Red"et n'a aucun effet sur l'argument transmis (qui est maintenant masqué, caradoit faire référence au nom local à partir de là). L'affectation n'est pas une opération sur place, que l'argument soit mutable ou immuable.

lebest une référence à un objet de liste mutable, et le.append()méthode effectue une extension sur place de la liste, en ajoutant le nouveau"Blue"valeur de chaîne.

(Étant donné que les objets chaîne sont immuables, ils n'ont aucune méthode qui prend en charge les modifications sur place.)

Une fois la fonction retournée, la réaffectation dean'a eu aucun effet, alors que l'extension debmontre clairement la sémantique des appels de style passe-par-référence.

Comme mentionné précédemment, même si l'argument pouraest un type mutable, la réaffectation dans la fonction n'est pas une opération sur place, et il n'y aurait donc pas de changement à 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 à la place le type de tuple immuable (identifié par les parenthèses sous la forme littérale, plutôt que par des crochets), qui ne prend pas en charge l'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'
La source
Quincy Lee
Translate

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

Sous le capot, Perl fonctionne efficacement en tant que référence. Les variables en tant qu'arguments d'appel de fonction sont transmises de manière référentielle, les constantes sont passées en tant que valeurs en lecture seule et les résultats des expressions sont passés en tant que temporaires. Les idiomes habituels pour construire des listes d'arguments par affectation de liste à partir de@_, ou parshiftont tendance à cacher cela à l'utilisateur, donnant l'apparence de pass-by-value:

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

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

Cela imprimeraValue is now 1parce que le$x++a incrémenté la variable lexicale déclarée dans leincr()Cette fonction passe par valeur est généralement ce que l'on souhaite la plupart du temps, car les fonctions qui modifient leurs arguments sont rares en Perl, et le style doit être évité.

Cependant, si pour une raison quelconque ce comportement est spécifiquement souhaité, il peut être obtenu en opérant directement sur des éléments du@_array, 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 imprimeraValue is now 2, parce que le$_[0]++expression incrémenté le réel$valuevariable. La façon dont cela fonctionne est que sous le capot@_n'est pas un vrai tableau comme la plupart des autres tableaux (comme le feraitmy @array), mais à la place ses éléments sont construits directement à partir des arguments passés à un appel de fonction. Cela vous permet de construire une sémantique passe-par-référence si cela est 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 faire cela en pratique, car Perl prend en charge les valeurs de référence; c'est-à-dire des 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. Ceci est une indication claire pour le lecteur sur le site d'appel, que la sémantique passe-par-référence est en vigueur.

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

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

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

La source
Translate

Il y a unbonne explication icipour .NET.

Beaucoup de gens sont surpris que les objets de référence soient réellement passés par valeur (à la fois en C # et en Java). C'est 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 passer une référence par référence, ce qui signifie que vous pouvez modifier l'endroit où un objet réel pointe.

La source
Translate

N'oubliez pas qu'il y a aussipasser par les noms, etpasser par valeur-résultat.

Le passage par valeur-résultat est similaire au passage par valeur, avec l'aspect ajouté que la valeur est définie dans la variable d'origine qui a été transmise en tant que paramètre. Il peut, dans une certaine mesure, éviter les interférences avec les variables globales. C'est apparemment mieux en mémoire partitionnée, où un passage par référence pourrait provoquer une erreur de page (Référence).

Passer par nom signifie que les valeurs ne sont calculées que lorsqu'elles sont réellement utilisées, plutôt qu'au début de la procédure. Algol a utilisé le mot de passe, mais un effet secondaire intéressant est qu'il est très difficile d'écrire une procédure d'échange (Référence). De plus, l'expression passée par nom est réévaluée à chaque accès, ce qui peut également avoir des effets secondaires.

La source
Translate

par valeur

  • est plus lent que par référence car le système doit copier le paramètre
  • utilisé pour l'entrée uniquement

par référence

  • plus rapide car seul un pointeur est passé
  • utilisé pour l'entréeetproduction
  • peut être très dangereux s'il est utilisé en conjonction avec des variables globales
La source
Hobart Lee
Translate

Tout ce que vous dites comme pass-by-value ou pass-by-reference doit être cohérent dans toutes les langues. La définition la plus courante et la plus cohérente utilisée dans les langages est qu'avec le passage par référence, vous pouvez passer une variable à une fonction "normalement" (c'est-à-dire sans prendre explicitement d'adresse ou quoi que ce soit du genre), et la fonction peutaffecter à(ne pas muter le contenu de) le paramètre à l'intérieur de la fonction et cela aura le même effet que l'assignation à la variable dans la portée appelante.

De ce point de vue, les langues sont regroupées comme suit; chaque groupe ayant la même sémantique de passage. Si vous pensez que deux langues ne doivent pas être mises dans le même groupe, je vous mets au défi de trouver un exemple qui les distingue.

La grande majorité des langues, y comprisC, Java, Python, Rubis, JavaScript, Schème, OCaml, ML standard, Go, Objectif c, Smalltalk, etc. sont tousvaleur par valeur uniquement. Passer une valeur de pointeur (certaines langues l'appellent une "référence") ne compte pas comme un passage par référence; nous ne nous soucions que de la chose passée, du pointeur, pas de la chose pointée.

Langues telles queC ++, C#, PHPsont par défaut pass-by-value comme les langages ci-dessus, mais les fonctions peuvent explicitement déclarer des paramètres pass-by-reference, en utilisant&ouref.

Perlest toujours pass-by-reference; cependant, dans la pratique, les gens copient presque toujours les valeurs après les avoir obtenues, les utilisant ainsi de manière pass-par-valeur.

La source
Kerr Lee
Translate

ConcernantJ, alors qu'il n'y a que, AFAIK, passant par valeur, il existe une forme de passage par référence qui permet de déplacer beaucoup de données. Vous passez simplement quelque chose connu sous le nom de locale à un verbe (ou une fonction). Il peut s'agir d'une instance d'une classe ou simplement d'un conteneur générique.

spaceused=: [: 7!:5 <
exectime =: 6!:2
big_chunk_of_data =. i. 1000 1000 100
passbyvalue =: 3 : 0
    $ y
    ''
)
locale =. cocreate''
big_chunk_of_data__locale =. big_chunk_of_data
passbyreference =: 3 : 0
    l =. y
    $ big_chunk_of_data__l
    ''
)
exectime 'passbyvalue big_chunk_of_data'
   0.00205586720663967
exectime 'passbyreference locale'
   8.57957102144893e_6

L'inconvénient évident est que vous devez connaître le nom de votre variable d'une manière ou d'une autre dans la fonction appelée. Mais cette technique peut déplacer un grand nombre de données sans douleur. C'est pourquoi, bien que techniquement pas passer par référence, je l'appelle "à peu près ça".

La source
Translate

PHP se passe également par valeur.

<?php
class Holder {
    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }
}

function swap($x, $y) {
    $tmp = $x;
    $x = $y;
    $y = $tmp;
}

$a = new Holder('a');
$b = new Holder('b');
swap($a, $b);

echo $a->getValue() . ", " . $b->getValue() . "\n";

Les sorties:

a b

Cependant, en PHP4, les objets étaient traités commeprimitives. Ce qui signifie:

<?php
$myData = new Holder('this should be replaced');

function replaceWithGreeting($holder) {
    $myData->setValue('hello');
}

replaceWithGreeting($myData);
echo $myData->getValue(); // Prints out "this should be replaced"
La source
Nat Lee
Translate

Par défaut, ANSI / ISO C utilise l'un ou l'autre - cela dépend de la façon dont vous déclarez votre fonction et de ses paramètres.

Si vous déclarez vos paramètres de fonction en tant que pointeurs, la fonction sera pass-by-reference, et si vous déclarez vos paramètres de fonction comme des variables non-pointeur, la fonction sera pass-by-value.

void swap(int *x, int *y);   //< Declared as pass-by-reference.
void swap(int x, int y);     //< Declared as pass-by-value (and probably doesn't do anything useful.)

Vous pouvez rencontrer des problèmes si vous créez une fonction qui renvoie un pointeur vers une variable non statique créée dans cette fonction. La valeur renvoyée du code suivant ne serait pas définie - il n'y a aucun moyen de savoir si l'espace mémoire alloué à la variable temporaire créée dans la fonction a été écrasé ou non.

float *FtoC(float temp)
{
    float c;
    c = (temp-32)*9/5;
    return &c;
}

Vous pouvez cependant renvoyer une référence à une variable statique ou à un pointeur passé dans la liste de paramètres.

float *FtoC(float *temp)
{
    *temp = (*temp-32)*9/5;
    return temp;
}
La source