2012-05-10 17 views
26

Was ist der sauberste Weg, um einen Fall wie das zu handhaben:Adresse eines temporären in Go?

func a() string { 
    /* doesn't matter */ 
} 

b *string = &a() 

Dies erzeugt den Fehler:

cannot take the address of a()

Mein Verständnis ist, dass Go fördert automatisch eine lokale Variable auf dem Heap, wenn die Adresse ist genommen. Hier ist klar, dass die Adresse des Rückgabewertes genommen werden muss. Was ist eine idiomatische Art, damit umzugehen?

+0

Was wollten Sie mit der fraglichen Konstruktion erreichen? – Wolf

Antwort

23

Der Adressenoperator gibt einen Zeiger auf etwas mit einem "Home" zurück, z. eine Variable. Der Wert des Ausdrucks in Ihrem Code ist "obdachlos". wenn Sie wirklich ein * String benötigen, werden Sie es in 2 Schritten zu tun haben:

tmp := a(); b := &tmp 

Hinweis, dass es zwar in vollem Umfang gültig Anwendungsfälle für * string, oft sind, ist es ein Fehler, sie zu benutzen. In Go string ist ein Werttyp, aber ein billiger Typ (ein Zeiger und ein Int). String Wert ist unveränderlich, ändert sich eine *string ändert, wo die "Heimat" zeigt, nicht der String-Wert, so in den meisten Fällen *string wird überhaupt nicht benötigt.

+1

oder geben Sie eine '* string' von' a() ' – thwd

+2

Sie können auch die Adresse eines Composite-Literal nehmen, die kein" home " – newacct

+0

Composite-Literal hat nicht unbedingt ein Zuhause, aber die Spezifikationen garantieren dass ein zusammengesetztes Literal, dessen Adresse vom '&' Operator genommen wird, ein "Home" hat (keine andere Option ist möglich, sonst würde es keine Adresse geben). – zzzz

0

a() zeigt nicht auf eine Variable, wie es auf dem Stapel ist. Du kannst nicht auf den Stapel zeigen (warum würdest du?).

Sie tun können, wenn Sie wollen

va := a() 
b := &va 

Aber was Ihr wirklich wollen, zu erreichen, ist etwas unklar.

+0

Nun, nein, jetzt glaube ich 'va' wird auf dem Haufen gespeichert werden. –

+0

Kann jemand bestätigen, dass im angegebenen Beispielcode 'va' auf dem Heap gespeichert wird, weil der Adressoperator in der folgenden Zeile steht? –

+0

Die eigentliche Frage scheint zu sein: "Wird der Wert von va durch den GC bereinigt, wenn der deklarierende Block verworfen wird, aber b noch existiert?". Ist es nicht? Ich denke, der Wert wird aufgrund des Zeigers in b nicht berücksichtigt. –

12

Siehe den entsprechenden Abschnitt der Go language spec.

  1. Etwas, das adressierbar ist: & kann nur verwendet werden, variable, pointer Indirektion, slice Indexierungsvorgang, Feldselektor eines adressierbaren struct, array Indexierungsvorgang eines adressierbaren Arrays; OR
  2. Verbund wörtliche

Was haben Sie keiner von denen ist, so dass es nicht funktioniert.

Ich bin nicht einmal sicher, was es bedeuten würde, selbst wenn Sie es tun könnten. Die Adresse des Ergebnisses eines Funktionsaufrufs nehmen? In der Regel übergeben Sie einen Zeiger von etwas an jemanden, weil Sie möchten, dass sie dem Objekt, auf das gezeigt wird, zuweisen können und die Änderungen in der ursprünglichen Variablen sehen können. Aber das Ergebnis eines Funktionsaufrufs ist temporär; niemand sonst "sieht" es, wenn Sie es nicht zuerst etwas zuweisen.

Wenn der Zweck des Erstellens des Zeigers ist, etwas mit einer dynamischen Lebensdauer zu erstellen, ähnlich wie new() oder die Adresse eines zusammengesetzten Literals, dann können Sie das Ergebnis des Funktionsaufrufs einer Variablen zuweisen und die Adresse von nehmen Das.

+0

Wenn eine Funktion einen Nicht-Zeiger-Wert zurückgibt, scheint es mir klar zu sein, dass angenommen wird, dass der Rückgabewert auf dem Stapel gespeichert ist.Aber wenn Sie sofort die Adresse des Rückgabewertes nehmen, sehe ich keine Unklarheit, Speicher für die Rückkehr auf den Haufen zu fördern, wie normal? Hell, da der Aufrufer-Frame noch keine Gelegenheit hatte, den Rückgabewert zu verwenden, könnte der Aufrufer das Ergebnis nach der Rückkehr einfach direkt in den Heap kopieren. –

+0

+1 für die Spezifikationsreferenz –

5

Am Ende Sie schlagen vor, dass Go sollten Sie die Adresse eines beliebigen Ausdruck, zum Beispiel nehmen lassen:

i,j := 1,2 
var p *int = &(i+j) 
println(*p) 

Die aktuellen Go-Compiler druckt der Fehler: cannot take the address of i + j

Meiner Meinung nach, Erlauben dem Programmierer, die Adresse eines beliebigen Ausdrucks zu nehmen:

  • Scheint nicht sehr nützlich zu sein (das heißt: es scheint eine sehr kleine Wahrscheinlichkeit zu haben, tatsächlich zu vorkommen Programme gehen).
  • Es würde den Compiler und die Sprachspezifikation komplizieren.

Es scheint kontraproduktiv, den Compiler und die Spezifikation für wenig Gewinn zu komplizieren.

+0

Hm das macht eigentlich sehr viel Sinn. Ich habe übersehen, dass der Funktionsaufruf ein Ausdruck ist. –

+0

Die Fehlermeldung scheint ziemlich klar darüber zu sein, wie Go interpretiert & (i + j), aber vielleicht nicht. Es wäre vernünftig (vielleicht nicht in Go) für einen Programmierer, dies zu interpretieren als "die Adresse des Wertes, der sich aus der Ausführung des Ausdrucks (i + j) ergibt", und einige interpretieren das tatsächlich so. (Ich habe keine Meinung darüber, ob Go richtig oder falsch ist ... aber ist 'a' ein Ausdruck? & A funktioniert dafür, '& (a)' funktioniert auch :-) Also, Go wendet sich gegen einen Ausdruck oder ein Stack zugeordneten Wert (und einfach mit dem Namen "i + j")? Ist es wichtig? – hutch

+1

@ Hutch Go (zumindest in der aktuellen Implementierung) widerspricht dem Ausdruck. Aus Sicht des Programmierers spielt es keine Rolle, wie der Compiler das tut. –

1

Ich war kürzlich in Knoten über etwas Ähnliches gebunden.

Zuerst spricht über Strings in Ihrem Beispiel eine Ablenkung ist, verwenden Sie eine Struktur statt, es zu etwas neu zu schreiben wie:

func a() MyStruct { 
    /* doesn't matter */ 
} 

var b *MyStruct = &a() 

Dies wird nicht kompilieren, weil Sie die Adresse eines nicht nehmen(). So tun:

func a() MyStruct { 
    /* doesn't matter */ 
} 

tmpA := a() 
var b *MyStruct = &tmpA 

Dies kompiliert, aber Sie haben einen MyStruct auf den Stapel zurück, ausreichend Platz auf dem Heap zugewiesen einen MyStruct zu speichern, kopiert dann die Inhalte aus dem Stapel auf dem Heap. Wenn Sie dies vermeiden wollen, dann schreiben Sie es wie folgt aus:

func a2() *MyStruct { 
    /* doesn't matter as long as MyStruct is created on the heap (e.g. use 'new') */ 
} 

var a *MyStruct = a2() 

Kopieren normalerweise billig ist, aber diese structs könnte groß sein. Noch schlimmer, wenn Sie die Struktur modifizieren und "bleiben" wollen, können Sie nicht kopieren und dann die Kopien modifizieren.

Wie auch immer, es macht umso mehr Spaß, wenn Sie einen Rückgabetyp der Schnittstelle {} verwenden. Die Schnittstelle {} kann die Struktur oder ein Zeiger auf eine Struktur sein. Das gleiche Kopierproblem kommt auf.

Verwandte Themen