Referenz übergeben oder Wert übergeben? [geschlossen]

  • Wenn Sie eine neue Programmiersprache lernen, ist eines der möglichen Hindernisse die Frage, ob es sich bei der Sprache standardmäßig um Wert-nach-Wert-Wert oder Pass-by-Referenz-Code handelt /em>.

    Hier ist also meine Frage an Sie alle in Ihrer bevorzugten Sprache: wie wird das eigentlich gemacht? Und was sind die möglichen Fallstricke ?

    Ihre Lieblingssprache kann natürlich alles sein, mit dem Sie je gespielt haben: beliebt , obscure , esoterisch , neu , alt ...

    21 July 2014
    Mark Byers
10 answers
  • Hier ist mein eigener Beitrag für die Java-Programmiersprache .

    Zuerst etwas Code:

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

    Der Aufruf dieser Methode führt zum Ergebnis in diesem:

     int pi = 3;
    int everything = 42;
    
    swap(pi, everything);
    
    System.out.println("pi: " + pi);
    System.out.println("everything: " + everything);
    
    "Output:
    pi: 3
    everything: 42"
     

    Selbst wenn Sie 'echte' Objekte verwenden, wird ein ähnliches Ergebnis angezeigt:

     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"
     

    Es ist also klar, dass Java seine Parameter mit Wert als Wert für pi übergibt und alles und die MyObj-Objekte werden nicht ausgetauscht. Beachten Sie, dass "by value" der einzige Weg ist in Java, um Parameter an eine Methode zu übergeben. (Zum Beispiel kann eine Sprache wie c ++ dem Entwickler einen Parameter als Referenz übergeben, indem nach dem Typ des Parameters " & amp; " verwendet wird.)

    jetzt die kniffliger Teil oder zumindest der Teil, der die meisten neuen Java-Entwickler verwirren wird: (geborgt bei javaworld )
    Ursprünglicher Autor: 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"
     

    tricky ändert erfolgreich den Wert von pnt1! Dies würde bedeuten, dass Objekte als Referenz übergeben werden. Dies ist nicht der Fall! A Richtige Anweisung wäre: Die Objektreferenzen werden als Wert übergeben.

    mehr von Tony Sintes:

    Die Methode ändert erfolgreich den - Wert von pnt1, auch wenn durch Wert übergeben wird. Ein Swap von pnt1 und pnt2 schlägt jedoch fehl! Dies ist die Hauptquelle der Verwirrung. In der Methode main () sind pnt1 und pnt2 nichts weiter als Objektreferenzen. Wenn Sie pnt1 und pnt2 an die tricky () -Methode übergeben, übergibt Java die Referenzen wie alle anderen Parameter mit dem Wert . Dies bedeutet, dass die Verweise, die an die -Methode übergeben werden, tatsächlich Kopien der ursprünglichen Verweise sind. Abbildung 1 unten zeigt zwei Referenzen

    05 August 2008
    EAMannAndrew
  • Python verwendet Pass-by-Value, da jedoch alle diese Werte Objektreferenzen sind, ist der Nettoeffekt so etwas wie Pass-by-Reference. Python-Programmierer denken jedoch mehr darüber nach, ob ein Objekttyp mutable oder immutable ist. Veränderliche Objekte können an Ort und Stelle geändert werden (z. B. Wörterbücher, Listen, benutzerdefinierte Objekte), nicht veränderliche Objekte (z. B. ganze Zahlen, Zeichenfolgen, Tupel).

    Das folgende Beispiel zeigt eine Funktion, der zwei Argumente, eine unveränderliche Zeichenfolge und eine veränderbare Liste übergeben werden.

     >>> 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']
     

    Die Zeile a = "Red" erstellt lediglich einen lokalen Namen a für den Zeichenfolgewert "Red" und hat keine Auswirkung auf das übergebene Argument (das jetzt ausgeblendet ist, da a sich auf den lokalen Namen beziehen muss auf). Die Zuweisung ist keine In-Place-Operation, unabhängig davon, ob das Argument veränderlich oder unveränderlich ist.

    Der Parameter b ist eine Referenz auf ein veränderliches Listenobjekt und das [&&6 && Die Methode] führt eine In-Place-Erweiterung der Liste durch und übernimmt den neuen Zeichenfolgenwert "Blue".

    (Da String-Objekte unveränderlich sind, verfügen sie nicht über entsprechende Methoden.) Vor-Ort-Änderungen unterstützen.)

    Nach der Rückkehr der Funktion hat die Neuzuweisung von a keine Auswirkung, während die Erweiterung von b eindeutig das Vorbeifahren anzeigt -berufende Aufrufsemantik.

    Auch wenn das Argument für a ein veränderlicher Typ ist, ist die Neuzuweisung innerhalb der Funktion keine In-Place-Operation An dem Wert des übergebenen Arguments würde sich also nichts ändern:

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

    Wenn Sie nicht möchten Um die durch die aufgerufene Funktion modifizierte Liste zu verwenden, verwenden Sie stattdessen den unveränderlichen Tupel-Typ (der durch die Klammern in der Literal-Form gekennzeichnet ist) quare-Klammern), die die In-Place-Methode .append() nicht unterstützen:

     >>> 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'
     
    23 August 2008
    yukondude
  • Da ich noch keine Perl-Antwort gesehen habe, dachte ich, ich würde eine schreiben.

    Unter der Haube arbeitet Perl effektiv als Pass-by- Referenz. Variablen als Funktionsaufrufargumente werden referenziell übergeben, Konstanten werden als schreibgeschützte Werte übergeben, und Ergebnisse von Ausdrücken werden als temporäre Variablen übergeben. Die gebräuchlichen Redewendungen zum Erstellen von Argumentlisten durch Listenzuweisung von @_ oder von shift neigen dazu, dies vor dem Benutzer zu verbergen und geben den Anschein eines Pass-by-Value:

     sub incr {
      my ( $x ) = @_;
      $x++;
    }
    
    my $value = 1;
    incr($value);
    say "Value is now $value";
     

    Dadurch wird Value is now 1 gedruckt, da $x++ die in der Funktion incr() deklarierte lexikalische Variable inkrementiert hat -by-value -Stil ist in der Regel am häufigsten erwünscht, da Funktionen, die ihre Argumente modifizieren, in Perl selten sind und der Stil vermieden werden sollte.

    Wenn jedoch für Aus irgendeinem Grund ist dieses Verhalten besonders erwünscht. Es kann durch direktes Arbeiten mit Elementen des Arrays @_ erreicht werden, da diese Aliase für in die Funktion übergebene Variablen sind.

     [pre> sub incr {
      $_[0]++;
    }
    
    my $value = 1;
    incr($value);
    say "Value is now $value";
     

    Dieses Mal wird Value is now 2 gedruckt, da der Ausdruck $_[0]++ die tatsächliche Variable $value erhöht hat. Das funktioniert so, dass unter der Haube @_ kein reales Array wie die meisten anderen Arrays ist (wie es von my @array erhalten würde), sondern seine Elemente werden direkt aus den Argumenten aufgebaut, die an einen Funktionsaufruf übergeben werden. Auf diese Weise können Sie die Referenz-Semantik erstellen, falls dies erforderlich wäre. Funktionsaufrufargumente, die reine Variablen sind, werden unverändert in dieses Array eingefügt, und Konstanten oder Ergebnisse komplexerer Ausdrücke werden als schreibgeschützte temporäre Elemente eingefügt.

    Es ist jedoch äußerst selten Um dies in der Praxis zu tun, weil Perl Referenzwerte unterstützt. dh Werte, die sich auf andere Variablen beziehen. Normalerweise ist es weitaus klarer, eine Funktion zu konstruieren, die eine offensichtliche Nebenwirkung auf eine Variable hat, indem eine Referenz auf diese Variable übergeben wird. Dies ist ein klarer Hinweis

    13 April 2012
    LeoNerd
  • Es gibt einen gute Erklärung hier für .NET.

    Viele Leute wundern sich, dass Referenzobjekte tatsächlich per Wert übergeben werden (in beiden Fällen) C # und Java). Es ist eine Kopie einer Stapeladresse. Dadurch wird verhindert, dass sich eine Methode ändert, wohin das Objekt tatsächlich zeigt, aber es ermöglicht einer Methode immer noch, die Werte des Objekts zu ändern. In C # ist es möglich, eine Referenz als Referenz zu übergeben, dh Sie können ändern, wohin ein Objekt tatsächlich zeigt.

    07 August 2008
    mrdenny
  • Vergessen Sie nicht, dass es auch Namen übergeben und Wert-Ergebnis gibt.

    Übergabe nach Wert-Ergebnis ähnelt der Übergabe nach Wert mit dem zusätzlichen Aspekt, dass der Wert in der ursprünglichen Variablen festgelegt ist, die als Parameter übergeben wurde. Es kann bis zu einem gewissen Grad Interferenzen mit globalen Variablen vermeiden. Im partitionierten Speicher ist es anscheinend besser, wenn ein Referenzzugriff einen Seitenfehler verursachen könnte ( Referenz ).

    Durch Namen übergeben bedeutet, dass die Werte nur berechnet werden, wenn sie tatsächlich verwendet werden, und nicht zu Beginn der Prozedur. Algol verwendete Pass-by-Name, aber ein interessanter Nebeneffekt ist, dass es sehr schwierig ist, ein Swap-Verfahren zu schreiben ( Referenz ). Außerdem wird der mit name übergebene Ausdruck bei jedem Zugriff erneut ausgewertet, was auch Nebenwirkungen haben kann.

    05 August 2008
    Matthew Schinckel
  • Was immer Sie als Wertübergabe oder Referenzübergabe sagen, muss über alle Sprachen hinweg konsistent sein. Die gebräuchlichste und konsistenteste Definition, die in allen Sprachen verwendet wird, besteht darin, dass Sie mit einer Referenzübergabe eine Variable "normal" an eine Funktion übergeben können (dh, ohne explizit die Adresse oder ähnliches zu übernehmen) und die Funktion zugewiesen werden kann (Ändern Sie nicht den Inhalt des Parameters) innerhalb der Funktion, und es hat den gleichen Effekt wie das Zuweisen der Variablen im aufrufenden Bereich.

    Aus dieser Ansicht Die Sprachen sind wie folgt gruppiert; Jede Gruppe hat die gleiche Semantik. Wenn Sie der Meinung sind, dass zwei Sprachen nicht in derselben Gruppe zusammengefasst werden sollten, fordere ich Sie auf, ein Beispiel zu nennen, das sie unterscheidet.

    Die große Mehrheit der Sprachen, einschließlich C , Java , Python , Ruby , JavaScript , Schema , OCaml , Standard ML , Los , Objective-C , Smalltalk usw sind alle nur Wertübergaben . Das Übergeben eines Zeigerwerts (einige Sprachen nennen es eine "Referenz") zählt nicht als Referenzübergabe. Wir sind nur besorgt über die vergebene Sache, den Zeiger, nicht die Sache, auf die verwiesen wurde.

    Sprachen wie C ++ , C # , PHP sind standardmäßig wie in den oben genannten Sprachen durchlaufend, aber Funktionen können Parameter explizit als Übergabebezug deklarieren, indem Sie & oder ref.

    verwenden

    Perl ist immer Referenzreferenz; In der Praxis werden die Werte jedoch fast immer kopiert, nachdem sie abgerufen wurden, und werden somit auf eine Wertepass-Weise verwendet.

    14 April 2012
    newacct
  • nach Wert

    • ist langsamer als durch Verweis, da das System den Parameter
    • wird nur für die Eingabe verwendet

    als Referenz

    • schneller, da nur ein Zeiger übergeben wird
    • für die Eingabe und -Ausgabe
    • kann sein sehr gefährlich, wenn sie zusammen mit globalen Variablen verwendet wird
    05 August 2008
    DShook
  • In Bezug auf J gibt es nur ein Formular, wenn es AFAIK gibt durch Referenz übergeben, wodurch viele Daten verschoben werden können. Sie übergeben einfach ein als Gebietsschema bekanntes Gebiet an ein Verb (oder eine Funktion). Dies kann eine Instanz einer Klasse oder nur ein generischer Container sein.

     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
     

    Der offensichtliche Nachteil ist, dass Sie dies benötigen um den Namen Ihrer Variablen in der aufgerufenen Funktion irgendwie zu kennen. Diese Technik kann jedoch viele Daten schmerzlos verschieben. Deshalb nenne ich es, obwohl es technisch nicht als Referenz gilt, "so ziemlich das".

    21 November 2009
    MPelletier
  • PHP wird auch per Wert übergeben.

     <?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";
     

    Ausgaben:

     a b
     

    Allerdings wurden in PHP4 Objekte wie Grundelemente . Was bedeutet:

     <?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"
     
    11 August 2008
    grom
  • Standardmäßig verwendet ANSI / ISO C eine der beiden Optionen. Dies hängt davon ab, wie Sie Ihre Funktion und ihre Parameter deklarieren.

    Wenn Sie Ihre Funktionsparameter als Zeiger deklarieren dann ist die Funktion pass-by-reference, und wenn Sie Ihre Funktionsparameter als Nicht-Pointer-Variablen deklarieren, wird die Funktion pass-by-value sein.

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

    Sie können auf Probleme stoßen, wenn Sie eine Funktion erstellen, die einen Zeiger auf eine nicht statische Variable zurückgibt, die in dieser Funktion erstellt wurde. Der zurückgegebene Wert des folgenden Codes wäre undefiniert. Es gibt keine Möglichkeit festzustellen, ob der Speicherbereich für die in der Funktion erstellte temporäre Variable überschrieben wurde oder nicht.

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

    Sie können jedoch einen Verweis auf eine statische Variable oder einen Zeiger zurückgeben, der in der Parameterliste übergeben wurde.

     float *FtoC(float *temp)
    {
        *temp = (*temp-32)*9/5;
        return temp;
    }
     
    13 January 2011
    padeoedrobison