Was ist der Unterschied zwischen
- ein als Referenz übergebener Parameter
- einen als Wert übergebenen Parameter?
Könnten Sie mir bitte einige Beispiele nennen?
Was ist der Unterschied zwischen
Könnten Sie mir bitte einige Beispiele nennen?
Zuallererst, die Unterscheidung zwischen "Wertübergabe" und "Referenzübergabe", wie sie in der CS-Theorie definiert ist, ist jetzt überholt というのも die ursprünglich als "pass by reference" definierte Technik ist inzwischen in Vergessenheit geraten und wird nur noch selten verwendet. 1
Neuere Sprachen 2 tendieren dazu, ein anderes (aber ähnliches) Paar von Techniken zu verwenden, um die gleichen Effekte zu erzielen (siehe unten), die die Hauptquelle der Verwirrung ist.
Eine weitere Quelle der Verwirrung ist die Tatsache, dass in "pass by reference" hat der Begriff "Referenz" eine engere Bedeutung als der allgemeine Begriff "Referenz" (denn der Satz ist älter als der Begriff).
Die authentische Definition lautet also:
Wenn ein Parameter durch Referenz übergeben der Anrufer und der Angerufene dieselbe Variable verwenden für den Parameter. Wenn der Aufrufende die Parametervariable ändert, ist die Auswirkung für die Variable des Aufrufenden sichtbar.
Wenn ein Parameter nach Wert übergeben haben der Anrufer und der Angerufene zwei unabhängige Variablen mit demselben Wert. Wenn der Aufrufende die Parametervariable ändert, ist die Auswirkung für den Aufrufenden nicht sichtbar.
Folgende Punkte sind bei dieser Definition zu beachten:
"Variable" bedeutet hier die (lokale oder globale) Variable des Aufrufers selbst -- d.h. wenn ich eine lokale Variable per Referenz übergebe und ihr zuweise, ändere ich die Variable des Aufrufers selbst, nicht z.B. das, worauf sie zeigt, wenn es ein Zeiger ist.
In modernen Sprachen haben Variablen in der Regel einen "Referenztyp". (ein weiteres Konzept, das später als "pass by reference" erfunden wurde und von diesem inspiriert ist), d.h. die eigentlichen Objektdaten werden separat irgendwo gespeichert (normalerweise auf dem Heap), und es werden immer nur "Referenzen" auf sie in Variablen gehalten und als Parameter übergeben. 3
Die Übergabe einer solchen Referenz fällt unter "pass-by-value weil der Wert einer Variablen technisch gesehen der Verweis selbst und nicht das verwiesene Objekt ist. Wie auch immer, Der Nettoeffekt auf das Programm kann derselbe sein wie bei "pass-by-value" oder "pass-by-reference":
Wie Sie sehen können, Dieses Paar von Techniken ist fast dasselbe wie die in der Definition, nur mit einer indirekten Ebene: Ersetzen Sie einfach "Variable" durch "referenziertes Objekt".
Es gibt keine einheitliche Bezeichnung für sie, was zu verdrehten Erklärungen wie "call by value where the value is a reference" führt. Im Jahr 1975 schlug Barbara Liskov den Begriff " Aufruf durch gemeinsame Nutzung von Objekten " (oder manchmal auch nur "Call-by-Sharing"), obwohl es sich nie richtig durchgesetzt hat. Außerdem weist keiner der beiden Begriffe eine Parallele zu dem ursprünglichen Paar auf. Kein Wunder, dass die alten Begriffe in Ermangelung eines besseren Begriffs wiederverwendet wurden, was zu Verwirrung führte. 4
(Ich würde die Begriffe "neu" o "Indirektes" Pass-by-Value/Pass-by-Reference für die neuen Techniken).
ANMERKUNG : Diese Antwort lautete lange Zeit:
Angenommen, ich möchte eine Webseite mit Ihnen teilen. Wenn ich Ihnen die URL mitteile, übergebe ich durch eine Referenz weiter. Sie können diese URL verwenden, um dieselbe Webseite zu sehen, die ich sehen kann. Wenn diese Seite geändert wird, sehen wir beide die Änderungen. Wenn Sie die URL löschen, zerstören Sie nur den Verweis auf diese Seite Seite - Sie löschen nicht die Seite selbst.
Wenn ich die Seite ausdrucke und Ihnen den Ausdruck gebe, komme ich an Wert. Ihre Seite ist eine abgekoppelte Kopie des Originals. Sie sehen keine Sie sehen keine nachträglichen Änderungen, und alle Änderungen, die Sie vornehmen (z. B. auf Ihrem Ausdruck) werden nicht auf der Originalseite angezeigt. Wenn Sie den Ausdruck zerstören, haben Sie Ihre Kopie des Objekts tatsächlich zerstört. Objekts zerstört - die Original-Webseite bleibt jedoch intakt.
C'est meist richtig außer die engere Bedeutung von "Referenz" - sie ist sowohl temporär als auch implizit (das muss sie nicht, aber explizit und/oder persistent zu sein sind zusätzliche Merkmale, die nicht Teil der Semantik der Weitergabe von Referenzen sind, wie oben erläutert). Eine nähere Analogie wäre, Ihnen eine Kopie eines Dokuments zu geben und Sie nicht aufzufordern, das Original zu bearbeiten.
1 Sofern Sie nicht in Fortran oder Visual Basic programmieren, ist dies nicht das Standardverhalten, und in den meisten heute verwendeten Sprachen ist ein echter Call-by-Reference nicht einmal möglich.
2 Auch eine ganze Reihe älterer Modelle unterstützen es
3 In mehreren modernen Sprachen sind alle Typen Referenztypen. Dieser Ansatz wurde 1975 von der Sprache CLU eingeführt und wurde seitdem von vielen anderen Sprachen übernommen, darunter Python und Ruby. Und viele weitere Sprachen verwenden einen hybriden Ansatz, bei dem einige Typen "Werttypen" und andere "Referenztypen" sind - darunter C#, Java und JavaScript.
4 Es ist nichts Schlechtes daran, einen passenden alten Begriff zu recyceln <em>per se, </em>aber man muss irgendwie deutlich machen, welche Bedeutung jeweils verwendet wird. Wenn man das nicht tut, ist das genau das, was immer wieder für Verwirrung sorgt.
Es ist eine Möglichkeit, Argumente an Funktionen zu übergeben. Die Übergabe per Referenz bedeutet, dass der Parameter der aufgerufenen Funktion derselbe ist wie das übergebene Argument des Aufrufers (nicht der Wert, sondern die Identität - die Variable selbst). Übergabe per Wert bedeutet, dass der Parameter der aufgerufenen Funktion eine Kopie des vom Aufrufer übergebenen Arguments ist. Der Wert ist derselbe, aber die Identität - die Variable - ist anders. Änderungen an einem Parameter durch die aufgerufene Funktion ändern also in einem Fall das übergebene Argument und im anderen Fall nur den Wert des Parameters in der aufgerufenen Funktion (die nur eine Kopie ist). In aller Eile:
ref
beim Aufrufer und bei der aufgerufenen Funktion verwendet). Jon Skeet hat auch eine schöne Erklärung dazu aquí .Codes
Da meine Sprache C++ ist, werde ich diese hier verwenden
// 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);
}
Und ein Beispiel in Java kann auch nicht schaden:
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;
}
}
Wikipedia
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference
Dieser Typ bringt es ziemlich genau auf den Punkt:
Viele Antworten hier (und insbesondere die am meisten hochgestimmte Antwort) sind sachlich falsch, da sie missverstehen, was "call by reference" wirklich bedeutet. Hier ist mein Versuch, die Dinge richtig zu stellen.
Vereinfacht ausgedrückt:
Um es metaphorisch auszudrücken:
Beachten Sie, dass diese beiden Konzepte völlig unabhängig und orthogonal zum Konzept der Referenztypen (das sind in Java alle Typen, die Untertypen von Object
und in C# alle class
Typen), oder das Konzept der Zeigertypen wie in C (die semantisch äquivalent zu den "Referenztypen" von Java sind, nur mit einer anderen Syntax).
Der Begriff der Referenztyp entspricht einer URL: Sie ist sowohl selbst eine Information als auch ein Referenz (a Zeiger (wenn Sie so wollen) zu anderen Informationen. Sie können viele Kopien einer URL an verschiedenen Orten haben, ohne dass sich die Website ändert, auf die sie alle verweisen; wenn die Website aktualisiert wird, führt jede URL-Kopie weiterhin zu den aktualisierten Informationen. Umgekehrt wirkt sich eine Änderung der URL an einer Stelle nicht auf die anderen Kopien der URL aus.
Beachten Sie, dass C++ einen Begriff von "Referenzen" hat (z.B.. int&
), das ist pas wie die "Referenztypen" von Java und C#, aber es wie "Aufruf durch Referenz". Die "Referenztypen" von Java und C# und tous Typen in Python sind wie das, was in C und C++ "Zeigertypen" genannt wird (z. B. int*
).
OK, hier ist die längere und formellere Erklärung.
Zunächst möchte ich einige wichtige Begriffe hervorheben, um meine Antwort zu verdeutlichen und um sicherzustellen, dass wir uns alle auf die gleichen Begriffe beziehen, wenn wir sie verwenden. (In der Praxis glaube ich, dass die überwiegende Mehrheit der Verwirrung bei Themen wie diesen darauf zurückzuführen ist, dass Wörter so verwendet werden, dass sie die beabsichtigte Bedeutung nicht vollständig vermitteln).
Zunächst ein Beispiel für eine Funktionsdeklaration in einer C-ähnlichen Sprache:
void foo(int param) { // line 1
param += 1;
}
Und hier ist ein Beispiel für den Aufruf dieser Funktion:
void bar() {
int arg = 1; // line 2
foo(arg); // line 3
}
Anhand dieses Beispiels möchte ich einige wichtige Begrifflichkeiten definieren:
foo
ist eine Funktion deklariert in Zeile 1 (Java besteht darauf, alle Funktionen zu Methoden zu machen, aber das Konzept ist dasselbe, ohne Verlust der Allgemeinheit; C und C++ unterscheiden zwischen Deklaration und Definition, auf die ich hier nicht eingehen werde)param
ist eine Formalparameter a foo
auch in Zeile 1 angegebenarg
ist eine variabel insbesondere eine lokale Variable der Funktion bar
, deklariert und initialisiert in Zeile 2arg
ist auch ein Argument zu einer bestimmten Aufruf von foo
in Zeile 3Hier sind zwei sehr wichtige Begriffspaare zu unterscheiden. Der erste ist Wert gegen variabel :
bar
Funktion, nach der Zeile int arg = 1;
ist der Ausdruck arg
hat die Wert 1
.final
oder C#s readonly
) oder tief unveränderlich (z.B. mit C++'s const
).Das andere wichtige Begriffspaar, das es zu unterscheiden gilt, ist Parameter gegen Argument :
En Aufruf nach Wert Bei den formalen Parametern der Funktion handelt es sich um Variablen, die für den Funktionsaufruf neu angelegt und mit der Option Werte ihrer Argumente.
Dies funktioniert genau so, wie alle anderen Arten von Variablen mit Werten initialisiert werden. Zum Beispiel:
int arg = 1;
int another_variable = arg;
Hier arg
y another_variable
sind völlig unabhängige Variablen - ihre Werte können sich unabhängig voneinander ändern. An dem Punkt jedoch, an dem another_variable
deklariert wird, wird es mit demselben Wert initialisiert, den arg
hält - und das ist 1
.
Da es sich um unabhängige Variablen handelt, sind Änderungen der another_variable
nicht beeinflussen arg
:
int arg = 1;
int another_variable = arg;
another_variable = 2;
assert arg == 1; // true
assert another_variable == 2; // true
Dies ist genau dasselbe wie die Beziehung zwischen arg
y param
in unserem obigen Beispiel, das ich hier aus Gründen der Symmetrie wiederholen möchte:
void foo(int param) {
param += 1;
}
void bar() {
int arg = 1;
foo(arg);
}
Es ist genau so, als hätten wir den Code auf diese Weise geschrieben:
// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here
Das heißt, das entscheidende Merkmal dessen, was Aufruf nach Wert bedeutet, dass der Angerufene ( foo
in diesem Fall) erhält Werte als Argumente, hat aber seine eigene separate Variablen für diese Werte aus den Variablen des Aufrufers ( bar
in diesem Fall).
Um auf meine obige Metapher zurückzukommen: Wenn ich bar
und Sie sind foo
Wenn ich Sie anrufe, gebe ich Ihnen einen Zettel mit einer Wert darauf geschrieben. Sie nennen das Stück Papier param
. Dieser Wert ist ein kopieren. des Wertes, den ich in mein Notizbuch geschrieben habe (meine lokalen Variablen), in eine Variable, die ich arg
.
(Nebenbei bemerkt: Je nach Hardware und Betriebssystem gibt es verschiedene Namenskonventionen darüber, wie man eine Funktion von einer anderen aus aufruft. Die Aufrufkonvention ist so, als ob wir uns entscheiden, ob ich den Wert auf ein Blatt Papier schreibe und es Ihnen dann gebe, oder ob Sie ein Blatt Papier haben, auf das ich es schreibe, oder ob ich es vor uns beiden an die Wand schreibe. Auch das ist ein interessantes Thema, würde aber den Rahmen dieser ohnehin schon langen Antwort bei weitem sprengen).
En Referenzaufruf sind die formalen Parameter der Funktion einfach neue Namen für die gleichen Variablen, die der Aufrufer als Argumente liefert.
Um auf unser obiges Beispiel zurückzukommen, ist das gleichbedeutend mit:
// entering function "bar" here
int arg = 1;
// entering function "foo" here
// aha! I note that "param" is just another name for "arg"
arg /* param */ += 1;
// exiting function "foo" here
// exiting function "bar" here
Desde param
ist nur ein anderer Name für arg
-- das heißt, sie sind die gleiche Variable , Änderungen an param
spiegeln sich wider in arg
. Dies ist der grundlegende Unterschied zwischen dem Aufruf über eine Referenz und dem Aufruf über einen Wert.
Nur sehr wenige Sprachen unterstützen den Aufruf per Verweis, aber C++ kann dies auf diese Weise tun:
void foo(int& param) {
param += 1;
}
void bar() {
int arg = 1;
foo(arg);
}
In diesem Fall, param
hat nicht nur die gleichen Wert als arg
ist es tatsächlich es arg
(nur unter einem anderen Namen) und so bar
kann feststellen, dass arg
wurde inkrementiert.
Beachten Sie, dass es sich hierbei um pas wie Java, JavaScript, C, Objective-C, Python oder fast alle anderen heute gängigen Sprachen funktionieren. Das bedeutet, dass diese Sprachen pas Aufruf durch Verweis, sie sind Aufruf durch Wert.
Wenn Sie Folgendes haben Aufruf nach Wert , aber der tatsächliche Wert ist a Referenztyp o Zeigertyp ist der "Wert" selbst nicht sehr interessant (in C ist er z.B. nur eine ganze Zahl mit einer plattformspezifischen Größe) - interessant ist, was dieser Wert zeigt auf .
Wenn der Referenztyp (d. h. der Zeiger) auf Folgendes verweist änderbar ist ein interessanter Effekt möglich: Sie können den Wert, auf den der Zeiger zeigt, ändern, und der Aufrufer kann Änderungen am Wert, auf den der Zeiger zeigt, beobachten, obwohl der Aufrufer Änderungen am Zeiger selbst nicht beobachten kann.
Um noch einmal die Analogie der URL zu bemühen: Die Tatsache, dass ich Ihnen eine kopieren. der URL einer Website ist nicht sonderlich interessant, wenn wir uns beide für die Website und nicht für die URL interessieren. Die Tatsache, dass Sie Ihre Kopie der URL überschreiben, hat keinen Einfluss auf meine Kopie der URL (und in Sprachen wie Java und Python kann die "URL" oder der Wert des Referenztyps überhaupt nicht geändert werden, sondern nur das, worauf sie verweist).
Als Barbara Liskov die CLU-Programmiersprache erfand (die diese Semantik hatte), erkannte sie, dass die bestehenden Begriffe "call by value" und "call by reference" nicht besonders nützlich waren, um die Semantik dieser neuen Sprache zu beschreiben. Also erfand sie einen neuen Begriff: Aufruf durch gemeinsame Nutzung von Objekten .
Wenn man über Sprachen spricht, die zwar technisch gesehen "call by value" sind, in denen aber üblicherweise Referenz- oder Zeigertypen verwendet werden (d.h. fast jede moderne imperative, objektorientierte oder multiparadigmatische Programmiersprache), finde ich es viel weniger verwirrend, wenn man es einfach vermeidet, über Aufruf nach Wert o Referenzaufruf . Einhalten Aufruf durch gemeinsame Nutzung von Objekten (oder einfach Aufruf durch Objekt ) und niemand wird verwirrt sein :-)
Bevor Sie diese beiden Begriffe verstehen, sollten Sie muss das Folgende verstehen. Jedes Objekt hat zwei Dinge, die es unterscheidbar machen können.
Wenn Sie also sagen employee.name = "John"
wissen, dass es zwei Dinge gibt, die name
. Sein Wert, der "John"
und auch seine Position im Speicher, die eine hexadezimale Zahl ist, vielleicht wie diese: 0x7fd5d258dd00
.
Abhängig von der Architektur der Sprache oder der Typ (Klasse, Struktur, usw.) Ihres Objekts, würden Sie entweder "John"
o 0x7fd5d258dd00
Weitergabe "John"
ist als Übergabe nach Wert bekannt.
Weitergabe 0x7fd5d258dd00
ist als Übergabe durch Verweis bekannt. Jeder, der auf diesen Speicherplatz verweist, hat Zugriff auf den Wert von "John"
.
Für weitere Informationen hierzu empfehle ich Ihnen die Lektüre von Dereferenzierung eines Zeigers und auch Warum sollte man struct (Werttyp) einer Klasse (Referenztyp) vorziehen? .
Hier ist ein Beispiel:
#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
}
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
0 Stimmen
Verwandt: Wie übergibt man Objekte an Funktionen in C++?
1 Stimmen
Wenn Sie nicht wissen, was ein Adresse o Wert ist dann siehe aquí