2010-05-14 17 views
34

Ich habe ein ziemlich gutes Verständnis des Dereferenzierungsoperators, der Adresse des Operators und der Zeiger im Allgemeinen.C++ Returning Pointer/Referenzen

ich aber verwirrt, wenn ich Sachen sehen wie folgt aus:

int* returnA() { 
    int *j = &a; 
    return j; 
} 

int* returnB() { 
    return &b; 
} 

int& returnC() { 
    return c; 
} 

int& returnC2() { 
    int *d = &c; 
    return *d; 
} 
  1. In returnA() Ich frage einen Zeiger zurück; nur um dies zu klären funktioniert, weil j ein Zeiger ist?
  2. In returnB() Ich möchte einen Zeiger zurückgeben; Da ein Zeiger auf eine Adresse zeigt, ist der Grund, warum returnB() funktioniert, weil ich &b zurückgeben?
  3. In returnC() Ich frage nach einer Adresse int zurückgegeben werden. Wenn ich c zurückgebe, wird der Operator & automatisch "angefügt" c?
  4. In returnC2() Ich bitte erneut um eine Adresse von int zurückgegeben werden. Funktioniert *d, weil Zeiger auf eine Adresse zeigen?

Angenommen, a, b, c werden als Ganzzahlen als Global initialisiert.

Kann jemand validieren, wenn ich mit allen meinen vier Fragen richtig bin?

+2

Die Antworten auf diese Fragen völlig abhängig auf die Deklarationen von a, b und c, die im Code fehlen. –

+0

Angenommen, a, b, c werden initialisiert. –

+0

Die Variablen sind Ints. –

Antwort

22

In returnA() Ich möchte einen Zeiger zurückgeben; nur um das zu verdeutlichen funktioniert das, weil j ein Zeiger ist?

Ja, int *j = &a initialisiert j-a zu zeigen. Dann geben Sie den Wert j zurück, das ist die Adresse a.

In returnB() möchte ich einen Zeiger zurückgeben; Da ein Zeiger auf eine Adresse zeigt, ist der Grund, warum returnB() funktioniert, weil ich & b zurückgeben?

Ja. Hier passiert dasselbe wie oben, nur in einem einzigen Schritt. &b gibt die Adresse b an.

In returnC() Ich frage nach einer Adresse von Int zurückgegeben werden. Wenn ich zurückkomme, wird der Operator & automatisch angehängt?

Nein, es ist ein Verweis auf ein int, das zurückgegeben wird. Eine Referenz ist keine Adresse, genauso wie ein Zeiger - es ist nur ein alternativer Name für eine Variable. Daher müssen Sie den Operator & nicht anwenden, um eine Referenz einer Variablen zu erhalten.

In returnC2() Ich frage wieder nach einer Adresse von Int zurückgegeben werden. Funktioniert * d, weil Zeiger auf eine Adresse zeigen?

Wieder ist es ein Verweis auf ein int, das zurückgegeben wird. *d bezieht sich auf die ursprüngliche Variable c (was auch immer das sein mag), auf die c zeigt.Und dies kann implizit in eine Referenz umgewandelt werden, genau wie in returnC.

Zeiger zeigen im Allgemeinen nicht auf eine Adresse (obwohl sie können - z. B. int** ist ein Zeiger auf Zeiger auf int). Zeiger sind eine Adresse von etwas. Wenn Sie den Zeiger wie something* deklarieren, ist das something die Sache, auf die Ihr Zeiger verweist. Also in meinem obigen Beispiel, int** deklariert einen Zeiger auf eine int*, die zufällig ein Zeiger selbst ist.

+0

Können Sie bitte eine schlüssige Antwort auf die dritte Ausgabe geben (die Funktion returnC())? Obwohl c keine Referenzvariable ist, warum akzeptiert die Funktion c als Rückgabewert, obwohl sie Referenzen benötigt und eine Ganzzahlvariable zurückgegeben wird? – chosentorture

+0

@ChosenTorture, Ich denke, Tyler hat es in seiner Antwort unten sehr schön erklärt, also habe ich es aufgegriffen und halte mich an das DRY-Prinzip :-) –

+0

Ich habe gelesen, was Tyler sorgfältig geschrieben hat. Entweder beantwortet er meine Frage nicht oder vielleicht verstehe ich etwas nicht. Ich bitte dich, zu lesen, was ich schreibe. Sie haben geschrieben, dass _No, es ist ein Verweis auf eine Int, die zurückgegeben wird_. Wenn ich also einen Funktionstyp zu "int &" mache, sagt das dem Programmierer, dass eine Referenz ** zurückgegeben ** werden soll oder dass eine Referenz ** zurückgegeben werden muss und wird **? Wenn der erste Fall wahr ist, sollte der Code nicht funktionieren. Aber weil es so ist, ist letzteres wahr. Alles was ich frage ist der Grund warum? Ist das ein impliziter Umwandlungsprozess oder etwas? – chosentorture

2

In returnC() und returnC2() werden Sie nicht aufgefordert, die Adresse zurückzugeben.

Beide Funktionen geben Verweise auf Objekte zurück.
Eine Referenz ist nicht die Adresse von irgendetwas, es ist ein alternativer Name von etwas (das kann bedeuten, dass der Compiler eine Adresse verwenden kann (oder nicht je nach Situation), um das Objekt darzustellen (alternativ kann es auch wissen, dass es im Register bleibt))).

Alles, was Sie wissen, dass eine Referenz auf ein bestimmtes Objekt zeigt.
Während eine Referenz selbst kein Objekt ist, ist sie nur ein alternativer Name.

+0

Hier wurde ich verwirrt; Ich dachte, ich würde Adressen zurückgeben; nicht "Aliase". –

1

Alle Ihre Beispiele erzeugen undefiniertes Laufzeitverhalten. Sie geben Zeiger oder Verweise auf Elemente zurück, die nach Ausführung aus der Funktion verschwinden.

Lassen Sie mich klarstellen:

int * returnA() 
{ 
    static int a; // The static keyword keeps the variable from disappearing. 
    int * j = 0; // Declare a pointer to an int and initialize to location 0. 
    j = &a;  // j now points to a. 
    return j;  // return the location of the static variable (evil). 
} 

In Ihrer Funktion die Variable j zugeordnet ist a ‚s temporären Speicherort zu zeigen. Beim Verlassen Ihrer Funktion verschwindet die Variable a, aber ihr früherer Standort wird über j zurückgegeben. Da a nicht mehr an der Position vorhanden ist, auf die j zeigt, tritt ein undefiniertes Verhalten beim Zugriff auf *j auf.

Variablen innerhalb von Funktionen sollten nicht durch Referenz oder Zeiger durch anderen Code geändert werden. Es kann passieren, obwohl es undefiniertes Verhalten erzeugt.

Pedantisch sollten die zurückgegebenen Zeiger so deklariert werden, dass sie auf konstante Daten zeigen. Die zurückgegebenen Referenzen sollten const sein:

const char * Hello() 
{ 
    static const char text[] = "Hello"; 
    return text; 
} 

Die obige Funktion gibt einen Zeiger auf konstante Daten zurück. Anderer Code kann auf die statischen Daten zugreifen (lesen), kann jedoch nicht geändert werden.

const unsigned int& Counter() 
{ 
    static unsigned int value = 0; 
    value = value + 1; 
    return value; 
} 

In der obigen Funktion wird die value auf Null auf den ersten Eintrag initialisiert. Alle folgenden Ausführungen dieser Funktion bewirken, dass value um eins erhöht wird. Die Funktion gibt einen Verweis auf einen konstanten Wert zurück. Dies bedeutet, dass andere Funktionen den Wert (aus der Ferne) wie eine Variable verwenden können (ohne einen Zeiger dereferenzieren zu müssen).

In meinem Denken wird ein Zeiger für einen optionalen Parameter oder ein Objekt verwendet. Eine Referenz wird übergeben, wenn das Objekt existieren muss. Innerhalb der Funktion bedeutet ein referenzierter Parameter, dass der Wert existiert, jedoch muss ein Zeiger vor der Dereferenzierung auf Null überprüft werden. Mit einer Referenz gibt es auch mehr Sicherheit, dass das Zielobjekt gültig ist. Ein Zeiger könnte auf eine ungültige Adresse (nicht null) zeigen und nicht definiertes Verhalten verursachen.

+0

Wenn Sie annehmen, dass 'a',' b' und 'c' Globals sind, ist der Beispielcode in Ordnung. Wenn sie innerhalb der Funktionen als statisch deklariert worden wären, wäre das auch in Ordnung. Statik wird nicht an temporären Orten ein- und ausgelagert. Ihre Standorte sind statisch. Sie können immer einen Zeiger oder eine Referenz auf eine statische Variable zurückgeben. Es ist nicht immer eine gute Idee (Re-Entrance und Multithreading), aber es funktioniert. –

1

Semantisch wirken Referenzen als Adressen.Sie sind jedoch syntaktisch die Aufgabe des Compilers, nicht Ihre, und Sie können eine Referenz so behandeln, als wäre sie das ursprüngliche Objekt, auf das sie verweist, einschließlich der Bindung anderer Referenzen an sie und der Bezugnahme auf das ursprüngliche Objekt. Sagen Sie in diesem Fall die Zeigerarithmetik.

Der Nachteil davon ist, dass Sie nicht ändern können, worauf sie sich beziehen - sie sind zur Konstruktionszeit gebunden.

60

Obwohl Peter Ihre Frage beantwortet hat, ist eine Sache, die Sie eindeutig verwirrt, die Symbole * und &. Das Schwierige dabei ist, dass Sie beide zwei verschiedene Bedeutungen haben, die mit Indirection zu tun haben (sogar ohne die dritte Bedeutung von * für Multiplikation und & für bitweise-und).

  • *, wenn sie als Teil eines Typ anzeigt verwendet, dass der Typ ein Zeiger ist: int ist ein Typ, so ist int* ein pointer-to-int-Typ und int** ist ein Zeiger-zu-Zeiger-zu-Int-Typ.

  • & Bei Verwendung als Teil einer Typ zeigt an, dass der Typ eine Referenz ist. int ist ein Typ, also ist int& eine Referenz-zu-Int (es gibt keine Referenz-Referenz). Referenzen und Zeiger werden für ähnliche Dinge verwendet, aber sie sind ziemlich unterschiedlich und nicht austauschbar. Eine Referenz wird am besten als Alias ​​oder alternativer Name für eine vorhandene Variable betrachtet. Wenn x ein int ist, können Sie einfach int& y = x zuweisen, um einen neuen Namen y für x zu erstellen. Danach können x und y austauschbar verwendet werden, um auf die gleiche ganze Zahl zu verweisen. Die zwei wichtigsten Implikationen davon sind, dass Referenzen nicht NULL sein können (da es eine Originalvariable geben muss), und dass Sie keinen speziellen Operator verwenden müssen, um den ursprünglichen Wert zu erhalten (weil es nur ein alternativer Name ist, kein Zeiger). Referenzen können auch nicht neu zugeordnet werden.

  • *, wenn sie als unärer Operator verwendet führt eine Operation namens dereferenzieren (die nichts mit Bezug zu tun hat Typen!). Diese Operation ist nur für Zeiger sinnvoll. Wenn Sie einen Zeiger dereferenzieren, erhalten Sie zurück, worauf er hinweist. Also, wenn ist ein Zeiger auf int, *p ist die int wird darauf hingewiesen.

  • & wenn sie als unärer Operator verwendet führt eine Operation adress von genannt. Das ist ziemlich selbsterklärend; Wenn x eine Variable ist, dann ist &x die Adresse x. Die Adresse einer Variablen kann einem Zeiger auf den Typ dieser Variablen zugewiesen werden. Wenn also x ein int ist, dann kann &x einem Zeiger vom Typ int* zugewiesen werden, und dieser Zeiger wird auf x zeigen. Z.B. Wenn Sie int* p = &x zuweisen, kann *p verwendet werden, um den Wert x abzurufen.

Also denken Sie daran, ist der Typ Suffix & für Referenzen und hat nichts mit dem unären operatory &, zu tun, die mit Zeigern mit immer Adressen für die Verwendung zu tun hat. Die beiden Anwendungen sind völlig unabhängig voneinander. Und * als Typ-Suffix deklariert einen Zeiger, während * als unärer Operator eine Aktion auf Zeigern ausführt.

+6

Danke für eine der klarsten Erklärungen, die ich bei diesen Operatoren gesehen habe. –

+0

Sehr klar, vielen Dank! – ChaoSXDemon

+0

Schließlich eine Lösung, die mir helfen kann, meinen Kopf für diese beiden Dinge zu wickeln – ryf9059

5

Tyler, das war sehr hilfreich Erklärung, ich einig Experiment tat Visual Studio-Debugger mit diesem Unterschied zu klären, noch weiter: -

int sample = 90; 
int& alias = sample; 
int* pointerToSample = &sample; 

Name     Address      Type 
&alias    0x0112fc1c {90}    int * 
&sample    0x0112fc1c {90}    int * 
pointerToSample  0x0112fc1c {90}    int * 
*pointerToSample 90      int 
alias 90          int & 
&pointerToSample  0x0112fc04 {0x0112fc1c {90}} int * * 

Speicheraufteilung

PointerToSample  Sample/alias 
_______________......____________________ 
0x0112fc1c |   | 90 | 
___________|___.....__|________|_______... 

[0x0112fc04] ...  [0x0112fc1c