Übergeben von Referenz- oder vorbei Wert?

stimmen
48

Wenn eine neue Programmiersprache zu lernen, eine der möglichen Hindernisse stoßen Sie könnte , ist die Frage , ob die Sprache ist, die standardmäßig pass-by-Wert oder Pass-by-reference .

So , hier ist meine Frage an sie alle, in der Sprache übersetzt, wie es tatsächlich getan? Und was sind die möglichen Gefahren ?

Ihre bevorzugte Sprache kann natürlich sein , alles , was Sie jemals mit gespielt haben: populär , dunkel , esoterisch , neu , alt ...

Veröffentlicht am 05/08/2008 um 09:56
quelle vom benutzer
In anderen Sprachen...                            


11 antworten

stimmen
31

Hier ist mein eigener Beitrag für die Programmiersprache Java .

zuerst einige Code:

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

Aufruf dieser Methode in dieser Folge wird:

int pi = 3;
int everything = 42;

swap(pi, everything);

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

"Output:
pi: 3
everything: 42"

Verwendung auch Objekte ‚echte‘ ein ähnliches Ergebnis zeigt:

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"

so ist es klar , dass Java übergibt seine Parameter von Wert , da der Wert für pi und alles und die MyObj Objekte werden nicht getauscht. beachten Sie, dass „durch den Wert“ ist der einzige Weg , in Java - Parameter auf ein Verfahren zu übergeben. (zum Beispiel eine Sprache wie C ++ ermöglicht es dem Entwickler , der einen Parameter in Bezug auf Durchgang unter Verwendung ‚ & ‘ nach der Art des Parameters)

jetzt der schwierige Teil , oder zumindest der Teil, der die meisten der neuen Java - Entwickler verwirren: (entlehnt aus Javaworld )
Original - 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, ist dies nicht der Fall! Eine richtige Erklärung wäre: die Objektreferenzen werden von Wert übergeben.

mehr von Tony Sintes:

Das Verfahren ändert erfolgreich den Wert von PNT1, auch wenn es von Wert übergeben wird; jedoch nicht ein Tausch von PNT1 und PNT2! Das ist die wichtigste Quelle der Verwirrung. In der Methode main (), PNT1 und PNT2 sind nichts anderes als Objektreferenzen. Wenn Sie PNT1 und PNT2 zum tricky () Methode übergeben, übergibt Java die Referenzen von Wert wie jeder andere Parameter. Das bedeutet, die Bezugnahmen auf die Methode übergeben tatsächlich Kopien der ursprünglichen Referenzen sind. Abbildung 1 zeigt zwei Referenzen auf das gleiche Objekt zeigen, nachdem Java ein Objekt auf ein Verfahren durchläuft.

Abbildung 1 http://www.javaworld.com/javaworld/javaqa/2000-05/images/03-qa-0512-pass2b.gif

Fazit oder eine lange Geschichte kurz:

  • Java übergibt sie die Parameter von Wert
  • „durch den Wert“ ist der einzige Weg , in Java einen Parameter auf ein Verfahren passieren
  • unter Verwendung von Methoden aus dem Objekt als Parameter angegeben werden verändert , das Objekt als die Verweise auf die ursprünglichen Objekte verweisen. (wenn das Verfahren selbst ändert einige Werte)

Nützliche Links:

Beantwortet am 05/08/2008 um 09:56
quelle vom benutzer

stimmen
20

Hier ist ein weiterer Beitrag für die Programmiersprache C #

c # gibt ihre Argumente von Wert (Standard)

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

Diese Version von Swap-Aufruf wird also kein Ergebnis hat:

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

"output: 
x: foo
y: bar"

aber im Gegensatz zu java c # tut dem Entwickler die Möglichkeit, Parameter zu übergeben durch Bezugnahme wird dies durch Verwendung des ‚ref‘ Schlüsselwort vor dem Typ des Parameters erfolgen:

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

diese Swap wird der Wert des referenzierten Parameter ändern:

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

"output: 
x: bar
y: foo"

c # hat auch ein outes Schlüsselwort , und die Differenz zwischen ref und out ist ein subtiler ein. von Msdn:

Der Anrufer eines Verfahrens , das ein nimmt out - Parameter wird nicht als die Out - Parameter vor dem Aufruf übergeben an die Variablen zuzuweisen erforderlich; jedoch wird der Angerufene auf die Out - Parameter zuzuweisen erforderlich vor der Rückkehr.

und

Im Gegensatz ref Parameter werden betrachtet zunächst zugewiesen durch den Angerufenen. Als solches wird die Angerufene nicht auf die ref zuweisen erforderlich vor der Verwendung Parameter. Ref - Parameter werden beide in eine und aus einer Methode übergeben.

ein kleiner pitfall ist, wie in Java, dass Objekte , die von Wert übergeben noch geändert werden kann , ihre innere Methoden

Fazit:

  • c # gibt seine Parameter, die standardmäßig durch den Wert
  • aber bei Bedarf Parameter können auch übergeben werden durch Verweis mit dem Schlüsselwort ref
  • Innen Methoden aus einem Parameter Wert übergeben wird ändern , das Objekt (wenn das Verfahren selbst ändert einige Werte)

Nützliche Links:

Beantwortet am 05/08/2008 um 17:40
quelle vom benutzer

stimmen
19

Python verwendet Pass-by-Wert, aber da alle diese Werte Objektreferenzen sind, der Nettoeffekt ist so etwas wie Pass-by-reference. Jedoch denken Python - Programmierer mehr darüber , ob ein Objekttyp ist wandelbar oder unveränderlich . Veränderbare Objekte können an Ort und Stelle (zB Wörterbücher, Listen, benutzerdefinierte Objekte), während unveränderliche Objekte nicht geändert werden kann (zB ganze Zahlen, Strings, Tupel).

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

>>> 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 Linie a = "Red"schafft lediglich einen lokalen Namen, afür die String - Wert "Red"und hat keine Auswirkungen auf die übergebenen in Argument (die jetzt verborgen ist, wie aauf den lokalen Namen dann auf verweisen). Die Zuordnung ist nicht ein direktes Betrieb, unabhängig davon , ob das Argument wandelbar oder unveränderlich.

Der bParameter ist ein Verweis auf ein veränderbares Listenobjekt, und das .append()Verfahren führt eine direkte Erweiterung der Liste, auf dem neuen Anheften "Blue"Zeichenfolge.

(Da String-Objekte unveränderlich sind, haben sie keine Methoden, die an Ort und Stelle Modifikationen unterstützen.)

Sobald die Funktion zurückkehrt, die Neuzuweisung ahat keine Wirkung, während die Ausdehnung der bdeutlich zeigt Pass-by-reference Stil Aufruf Semantik.

Wie bereits erwähnt, auch wenn das Argument für aeinen veränderlichen - Typ ist, ist die Neuzuordnung innerhalb der Funktion nicht ein direkten Betrieb, und so würde es keine Änderung des Wert das übergebenen Arguments sein:

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

Wenn Sie nicht Ihre Liste der aufgerufenen Funktion geändert mögen, müssen Sie stattdessen den unveränderlichen Tupel - Typen (identifiziert durch die Klammern im wörtlichen Form, anstatt eckige Klammern) verwenden, die nicht die in-place unterstützt .append()Methode:

>>> 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'
Beantwortet am 23/08/2008 um 17:50
quelle vom benutzer

stimmen
7

Da ich eine Perl habe Antwort noch nicht gesehen, dachte ich, ich schreibe würde.

Unter der Haube arbeitet Perl effektiv als Pass-by-reference. Variablen als Funktionsaufruf Argumente werden referenziell bestanden, sind Konstanten als schreibgeschützte Werte übergeben, und die Ergebnisse der Ausdrücke werden als Provisorien übergeben. Die üblichen Idiome aus Argumentlisten von Listenzuordnung zu erstellen @_, oder durch shiftdiese neigen dazu , von dem Benutzer zu verbergen, was das Aussehen von Pass-by-Wert:

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

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

Dies wird gedruckt , Value is now 1weil die $x++die lexikalische Variable innerhalb der deklarierten erhöht hat incr()Funktion, anstatt die Variable übergeben. Dieser Pass-by-Value - Stil ist in der Regel , was die meiste Zeit gesucht, wie Funktionen , die ihre Argumente modifizieren sind selten in Perl, und der Stil sollte vermieden werden.

Wenn aus irgendeinem Grund jedoch ist dieses Verhalten ausdrücklich erwünscht ist , kann es durch den Betrieb direkt auf die Elemente des erreicht werden @_Array, weil sie Aliasnamen für Variablen in die Funktion übergeben werden.

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

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

Diesmal wird es drucken Value is now 2, da der $_[0]++Ausdruck der tatsächlichen erhöht $valueVariable. Die Art und Weise dies funktioniert , ist , dass unter der Haube @_ist keine echtes Array wie die meisten anderen Arrays (wie durch erhalten würde my @array), sondern sind seine Elemente direkt aus den Argumenten für einen Funktionsaufruf übergeben gebaut. Auf diese Weise können Sie Pass-by-reference Semantik konstruieren , wenn das erforderlich wäre. Funktionsaufruf Argumente , die Klar Variablen sind eingefügt in dieses Array wie sie ist, und Konstanten oder Ergebnisse komplexer Ausdrücke werden als schreibgeschützt Provisorien eingelegt.

Es ist jedoch äußerst selten dies in der Praxis zu tun, weil Perl Referenzwert unterstützt; das heißt, die Werte, die auf andere Variablen beziehen. Normalerweise ist es viel klarer, eine Funktion zu konstruieren, die eine offensichtliche Nebenwirkung auf einer Variable hat, die von diesen Variablen in einem Referenzgeben. Dies ist ein klarer Hinweis für den Leser an der callsite, die Pass-by-reference Semantik ist wirksam.

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

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

Hier wird der \Operator liefert eine Referenz in der gleichen Weise , wie die &Adressoperator in C.

Beantwortet am 13/04/2012 um 16:33
quelle vom benutzer

stimmen
6

Es gibt eine gute Erklärung hier für .NET.

Viele Leute sind überrascht, dass die Referenzobjekte werden von Wert (in C # und Java) tatsächlich weitergegeben. Es ist eine Kopie eines Stapels Adresse. Dies verhindert, dass ein Verfahren ändert, wo das Objekt tatsächlich Punkte, aber immer noch erlaubt eine Methode, die Werte des Objekts zu ändern. In C # dessen möglich, eine Referenz durch Bezugnahme passieren, was bedeutet, dass Sie, wo eine tatsächliche Objektpunkte ändern.

Beantwortet am 06/08/2008 um 23:43
quelle vom benutzer

stimmen
5

Vergessen Sie nicht , es ist auch mit Namen übergeben , und vorbei wert Ergebnis .

Pass von Wert-Ergebnis ist ähnlich durch den Wert übergeben, mit dem zusätzlichen Aspekt , dass der Wert in der ursprünglichen Variablen eingestellt wird , die als Parameter übergeben wurde. Es kann bis zu einem gewissen Grad, Vermeidung von Interferenzen mit globalen Variablen. Es ist offensichtlich besser in partitionierten Speicher, in dem ein Pass von Referenz einen Seitenfehler ( die dazu führen könnten Referenz ).

Pass nach Namen bedeutet , dass die Werte werden nur dann berechnet , wenn sie tatsächlich verwendet werden, anstatt zu Beginn des Verfahrens. Algol verwendete Pass-by-Name, aber ein interessanter Nebeneffekt ist , dass es sehr schwierig , ein Swap - Verfahren (schreiben Reference ). Auch der Ausdruck mit Namen übergeben wird neu bewertet jedes Mal auf sie zugegriffen wird, die auch Nebenwirkungen haben kann.

Beantwortet am 05/08/2008 um 11:00
quelle vom benutzer

stimmen
4

Was auch immer Sie sagen , als Pass-by-Wert oder Pass-by-reference muss über Sprachen konsistent sein. Die häufigste und einheitliche Definition für Sprachen verwendet wird , ist , dass mit Pass-by-reference, eine Variable an eine Funktion kann „normal“ (dh ohne Adresse oder so etwas ausdrücklich unter), und die Funktion kann zuweisen (nicht mutieren den Inhalt) die Parameter innerhalb der Funktion , und es wird die gleiche Wirkung wie die Zuweisung an die Variable in dem Aufruf Umfang hat.

Aus dieser Sicht sind die Sprachen wie folgt gruppiert; jede Gruppe die gleiche Durchgang Semantik aufweist. Wenn Sie denken, dass zwei Sprachen sollten nicht in derselben Gruppe gestellt werden, fordere ich Sie mit einem Beispiel zu entwickeln, die sie auszeichnet.

Die überwiegende Mehrheit der Sprachen , darunter C , Java , Python , Ruby - , JavaScript , Scheme , OCaml , Standard ML , Go , Objective-C , Smalltalk , etc. sind alle Pass-by-Wert nur . Einen Zeigerwert Passing ( in einigen Sprachen nennen es eine „Referenz“) zählt nicht als Pass von Referenz; wir sind nur besorgt über die Sache übergeben, der Zeiger, nicht das , was gezeigt wird.

Sprachen wie C ++ , C # , PHP sind standardmäßig pass-by-Wert wie die oben genannten Sprachen, aber Funktionen können explizit Parameter erklären sein pass-by-reference, mit &oder ref.

Perl ist immer Pass-by-reference; jedoch in der Praxis kopieren Menschen fast immer die Werte , nachdem sie bekommen, so dass es in einem Pass-by-Wert Art und Weise verwendet wird .

Beantwortet am 13/04/2012 um 21:00
quelle vom benutzer

stimmen
4

von Wert

  • Referenz ist langsamer als durch, da das System den Parameter zu kopieren hat
  • nur für die Eingabe verwendet

durch Bezugnahme

  • schneller, da nur ein Zeiger übergeben
  • für die Eingabe verwendet und Ausgang
  • kann sehr gefährlich sein, wenn in Verbindung mit globalen Variablen verwendet
Beantwortet am 05/08/2008 um 10:10
quelle vom benutzer

stimmen
3

Bezüglich J , während es nur ist, AFAIK, vorbei an Wert, ist es eine Form unter Bezugnahme der Weitergabe , die eine Menge von Daten ermöglicht bewegen. Sie passieren einfach etwas als locale zu einem Verb bekannt (oder Funktion). Es kann eine Instanz einer Klasse sein oder nur ein generischer Container.

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 den Namen Ihrer Variablen in irgendeiner Weise in der aufgerufenen Funktion wissen müssen. Aber diese Technik kann schmerzlos eine Menge Daten bewegen. Deshalb, während technisch nicht als Verweis übergeben, ich nenne es „so ziemlich die“.

Beantwortet am 21/11/2009 um 18:14
quelle vom benutzer

stimmen
2

PHP wird auch von 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";

Ausgänge:

a b

Jedoch in PHP4 Objekte wurden wie behandelt Primitiven . 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"
Beantwortet am 11/08/2008 um 03:21
quelle vom benutzer

stimmen
-1

In der Standardeinstellung ANSI / ISO C verwendet entweder - es hängt davon ab, wie Sie Ihre Funktion und ihre Parameter deklarieren.

Wenn Sie Ihre Funktionsparameter als Zeiger deklarieren, dann wird die Funktion seines pass-by-reference, und wenn Sie Ihre Funktionsparameter als nicht-Pointer-Variablen deklarieren, dann wird die Funktion seines Pass-by-Wert.

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 Probleme auftreten, wenn Sie eine Funktion erstellen, die einen Zeiger auf eine nicht-statische Variable zurückgibt, die innerhalb dieser Funktion erstellt wurde. Der Rückgabewert der folgende Code nicht definiert werden - es gibt keine Möglichkeit, wenn der Speicherplatz auf der temporären Variablen in der Funktion erstellt zugeordnet zu wissen, wurde überschrieben oder nicht.

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

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

float *FtoC(float *temp)
{
    *temp = (*temp-32)*9/5;
    return temp;
}
Beantwortet am 13/01/2011 um 21:48
quelle vom benutzer

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more