C ++: Unterschied zwischen kaufmännischem Und "&" und Sternchen "*" in der Funktions- / Methodendeklaration?

70

Gibt es einen subtilen Unterschied zwischen diesen:

void a1(float &b) {
    b=1;
};
a1(b);

und

void a1(float *b) {
    (*b)=1;
};
a1(&b);

?

Beide machen dasselbe (oder so scheint es aus main ()), aber der erste ist offensichtlich kürzer, jedoch verwendet der größte Teil des Codes, den ich sehe, die zweite Notation. Ist da ein Unterschied? Vielleicht für den Fall, dass es sich um ein Objekt handelt, anstatt zu schweben?

Slava V.
quelle
1
Versuchen Sie nun herauszufinden, was void a1 (float && b) in C ++ 0x bedeutet. :)
Daniel Earwicker
Es wäre schön, wenn es auch erklärt würde, warum ein LHS & in einigen Situationen * vorzuziehen ist oder sogar überhaupt notwendig ist, wenn wir * für Referenzen haben.
T. Webster
@ T.Webster: Referenzen sind unveränderlich. Das heißt, wenn Sie eines erstellen, bezieht es sich definitiv auf etwas (kann also nicht NULL sein, der Compiler zwingt Sie, explizit anzugeben, worauf es sich bezieht), und wenn Sie es einmal erstellt haben, kann es nie mehr auf etwas anderes verweisen. Sie sind sicherer, weil Sie nicht versehentlich alberne Dinge wie Zeigerarithmetik machen können, wenn Sie das nicht wollten.
Sam Svenbjorgchristiensensen
1
@Flow Ich denke, dass diese Frage bleiben sollte, da ich genau nach diesem Punkt gesucht habe und nicht gewusst hätte, dass sich einer auf Zeiger und einer auf das Übergeben als Referenz bezog, wenn ich diese Seite nicht besucht hätte. Vielen Dank für den Link zur anderen Frage!
Resquiens

Antworten:

56

Beide machen dasselbe, aber man verwendet Referenzen und man verwendet Zeiger.

In meiner Antwort hier finden Sie eine umfassende Liste aller Unterschiede .

Brian R. Bondy
quelle
1
Upvoted auch nicht mein "-1". Vielleicht hatte jemand das Gefühl, dass dies nicht genug erklärt. Danke für die Antwort!
Slava V
Toller Beitrag, habe seit ein paar Stunden Probleme und nachdem ich deine Zusammenfassung gelesen hatte, wurde ich mit einem "Eureka" -Moment gesegnet;) Danke @DanielSloof!
Polyklick
26
Ich würde in Betracht ziehen, dies abzustimmen, da es nichts weiter als ein Link ist. Antworten auf SO sollen für sich allein stehen, wobei Links als Unterstützung / Referenz verwendet werden - nicht als gesamte Antwort. So wie es ist, stimme ich nicht ab, weil es auf eine andere SO-Antwort verweist, die mein großes Problem mit dem Posten von Links als Probleme zunichte macht: Websites können und müssen sich im Laufe der Jahre ändern oder fallen! Aber natürlich, wenn eine Antwort sinken würde, wäre es wahrscheinlich, dass die andere dies auch tun würde, also werde ich sie passieren lassen und einfach nicht darüber abstimmen.
ArtOfWarfare
26

Ja. Die *Notation besagt, dass das, was auf dem Stapel übergeben wird, ein Zeiger ist, dh eine Adresse von etwas. Das &sagt, es ist eine Referenz. Der Effekt ist ähnlich, aber nicht identisch:

Nehmen wir zwei Fälle:

   void examP(int* ip);
   void examR(int& i);

   int i;

Wenn ich anrufe examP , schreibe ich

   examP(&i);

Dies nimmt die Adresse des Elements und übergibt sie an den Stapel. Wenn ich anrufeexamR ,

   examR(i);

Ich brauche es nicht; jetzt übergibt der Compiler "irgendwie" eine Referenz - was praktisch bedeutet, dass er die Adresse von erhält und übergibt i. Auf der Codeseite also

   void examP(int* ip){
        *ip += 1;
   }

Ich muss sicherstellen, dass der Zeiger dereferenziert wird. ip += 1macht etwas ganz anderes.

   void examR(int& i){
        i += 1;
   }

aktualisiert immer den Wert von i.

Weitere Informationen finden Sie unter "Call by Reference" im Vergleich zu "Call by Value". Der &Begriff gibt C ++ Aufruf als Referenz.

Charlie Martin
quelle
Charlie Martin oder jemand, der diese Antwort versteht, würden Sie dies bitte umformulieren / umschreiben, um eine präzisere Antwort zu erhalten? Speziell die beiden Beispielfälle. Ich glaube, dass es hier wertvolle Informationen gibt, und ich glaube, dass diese Antwort und die Benutzer von einer präziseren Antwort profitieren würden. Ich würde die Antwort selbst bearbeiten, aber ich verstehe die beiden Beispielfälle nicht.
HappyCoding
Ich denke, Sie sollten Stroustrup besser lesen. Ich beschuldige Sie nicht, verwirrt zu sein.
Charlie Martin
5

Im ersten Beispiel mit Referenzen wissen Sie, dass bdies nicht NULL sein kann. Mit dem Zeigerbeispielb könnte der NULL-Zeiger sein.

Doch diese Note es ist möglich , ein NULL - Objekt über eine Referenz zu übergeben, aber es ist umständlich und die aufgerufene Prozedur übernehmen kann , es ist ein Fehler geschehen:

a1(*(float *)NULL);
Greg Hewgill
quelle
3

Im zweiten Beispiel der Anrufer dem Variablennamen '&' voranstellen, um die Adresse der Variablen zu übergeben.

Dies kann von Vorteil sein - der Aufrufer kann eine Variable nicht versehentlich ändern, indem er sie als Referenz übergibt, wenn er glaubt, einen Wert zu übergeben.

finnw
quelle
3

Abgesehen von syntaktischem Zucker besteht der einzige wirkliche Unterschied darin, dass ein Funktionsparameter, der ein Zeiger ist, null sein kann. Daher kann die Zeigerversion aussagekräftiger sein, wenn sie den Nullfall richtig behandelt. Der Null-Fall kann auch eine besondere Bedeutung haben. Die Referenzversion kann nur mit Werten des angegebenen Typs ohne Nullfunktion betrieben werden.

jmucchiello
quelle
1

In Ihrem Beispiel tun beide Versionen dasselbe.

Das erste hat den Vorteil, dass es auf der Anrufseite transparent ist. Stellen Sie sich vor, wie es für einen Operator aussehen würde:

cin >> &x;

Und wie hässlich es für einen Swap-Aufruf aussieht

swap(&a, &b);

Sie möchten a und b tauschen. Und es sieht viel besser aus, als wenn Sie zuerst die Adresse nehmen müssen. Bjarne stroustrup schreibt übrigens, dass der Hauptgrund für Referenzen die Transparenz war, die auf der Anrufseite hinzugefügt wurde - insbesondere für Betreiber. Sehen Sie auch, wie es nicht mehr offensichtlich ist, ob das Folgende

&a + 10

Würde dem Inhalt von a 10 hinzufügen, den Operator + davon aufrufen oder ob es 10 zu einem temporären Zeiger auf a hinzufügt. Fügen Sie dies zu der Unmöglichkeit hinzu, dass Sie Operatoren nicht nur für eingebaute Operanden (wie einen Zeiger und eine Ganzzahl) überladen können. Referenzen machen dies kristallklar.

Zeiger sind nützlich, wenn Sie eine "Null" setzen möchten:

a1(0);

Dann kann die Methode in a1 den Zeiger mit 0 vergleichen und sehen, ob der Zeiger auf ein Objekt zeigt.

Johannes Schaub - litb
quelle
swap (int & a, int & b) kann nicht feststellen, dass der Aufruf mit identischen Argumenten getätigt wurde: swap (x, x); und früh rausspringen. Die Zeigerversion kann: if (a == b) return; Ich bin mir nicht sicher, ob dies wichtig ist, aber es ist ein Fall, den die Referenzversion nicht verarbeiten kann.
jmucchiello
Joe, es kann: wenn (& a == & b) zurückkehren;
Johannes Schaub - litb
0

Ein großer Unterschied ist, was draußen los ist. Sie haben entweder:

a1(something);

oder:

a1(&something);

Ich mag es, Argumente als Referenz zu übergeben (immer eine konstante :)), wenn sie in der Funktion / Methode nicht geändert wurden (und dann können Sie auch automatische / temporäre Objekte darin übergeben) und sie per Zeiger übergeben, um den Benutzer zu kennzeichnen und zu alarmieren. Leser des Codes, der die Methode aufruft, mit der das Argument möglicherweise absichtlich geändert wird.

RnR
quelle