2013-05-16 18 views
29

Die Go-Tour in diesem Beispiel für die Kanäle hat: http://tour.golang.org/#63Sind Kanäle durch Verweis übergeben implizit

package main 

import "fmt" 

func sum(a []int, c chan int) { 
    sum := 0 
    for _, v := range a { 
     sum += v 
    } 
    c <- sum // send sum to c 
} 

func main() { 
    a := []int{7, 2, 8, -9, 4, 0} 

    c := make(chan int) 
    go sum(a[:len(a)/2], c) 
    go sum(a[len(a)/2:], c) 
    x, y := <-c, <-c // receive from c 

    fmt.Println(x, y, x+y) 
} 

Der Kanal c wird in der Summenfunktion geändert und die Änderungen bestehen bleiben, nachdem die Funktion beendet hat. Offensichtlich wurde c als Referenz übergeben, aber es wurde kein Zeiger auf c erzeugt. Werden Kanäle implizit durch Verweis in go übergeben?

+0

Ja, die Referenztypen in Go sind 'slice',' map' und 'channel'. Wenn Sie diese übergeben, erstellen Sie eine Kopie der Referenz. * (Strings werden auch als Referenztyp implementiert, obwohl sie unveränderlich sind.) * –

Antwort

41

Technisch werden sie kopiert, weil wenn Sie make verwenden, Sie etwas auf dem Haufen zuweisen, also ist es technisch ein Zeiger hinter den Kulissen. Da der Zeigertyp jedoch nicht verfügbar ist, kann er als Referenztyp betrachtet werden.

EDIT: Vom spec:

Der integrierte Funktion Make einen Typ T erfolgt, die eine Scheibe, Karte oder Kanal-Typ sein muss, gegebenenfalls gefolgt von einer typspezifischen Liste von Ausdrücke. Es gibt einen Wert vom Typ T (nicht * T) zurück. Der Speicher wird wie im Abschnitt Anfangswerte beschrieben initialisiert.

Ein Kanal muss initialisiert werden, bevor er verwendet werden kann. Make tut dies, damit es als Referenztyp verwendet werden kann.

Was bedeutet dies im Grunde, dass Sie es in eine Funktion übergeben und schreiben oder lesen können. Die allgemeine Faustregel lautet: Wenn Sie make, new oder & verwenden, können Sie sie an eine andere Funktion übergeben, ohne die zugrunde liegenden Daten zu kopieren.

So sind die folgenden "Referenz" Typen:

  • Scheiben
  • Karten
  • Kanäle
  • Zeiger
  • Funktionen

nur die Datentypen (Zahlen, bools und Strukturen usw.) werden beim Übergang in eine Funktion kopiert. Zeichenfolgen sind speziell, weil sie unveränderlich sind, aber nicht nach Wert weitergegeben werden. Dies bedeutet, dass Folgendes nicht wie erwartet funktioniert:

type A struct { 
    b int 
} 
func f(a A) { 
    a.b = 3 
} 
func main() { 
    s := A{} 
    f(s) 
    println(s.b) // prints 0 
} 
+2

@tjameson: make bedeutet keine Heap-Zuweisung und Slice wird tatsächlich als Struktur implementiert und kopiert, wenn sie übergeben wird. – zzzz

+0

@squint - Richtig. Mit 'array' meinte ich' make ([] int, 5) ', aber ich habe gerade gemerkt, dass das technisch eher eine Scheibe ist. Mein Fehler. – tjameson

+0

@jnml - Überprüfung der Spezifikation, ich denke, das ist technisch nicht der Fall. Ich werde bearbeiten. – tjameson

1

Sie könnten ja sagen, aber die Aussage "Der Kanal c ist in der Summenfunktion modifiziert" ist nicht wirklich die richtige Terminologie. Send und Empfang von Kanälen werden nicht wirklich als Änderungen angesehen.

Beachten Sie, dass Slices und Maps sich ähnlich verhalten, siehe http://golang.org/doc/effective_go.html für weitere Details.

Auch "bestanden durch Verweis" impliziert, dass eine c in c in sum vorgenommen werden könnte, die ihren Wert (im Gegensatz zu den zugrunde liegenden Daten) außerhalb der Summe ändern würde, was nicht der Fall ist.

1

Kanalvariablen sind Referenzen, aber es hängt von Ihrer Definition von 'Referenz' ab. Language specification erwähnt nie Referenztypen.

Kein Kanal (Variable) ist in der Funktion sum "modifiziert". Das Senden an einen Kanal ändert seinen Status.

Mit anderen Worten, ja der Kanal ist als Zeiger auf eine Laufzeitstruktur implementiert. Beachten Sie, dass dies für die Referenzsemantik unbedingt erforderlich ist.

EDIT: Der obige Satz sollte lesen: "Beachten Sie, dass das ist nicht unbedingt für die Referenz-Semantik.", Dh. das Wort "nicht" ging MIA. Entschuldigung für eventuell entstandene Verwirrung.

5

Alles in Go wird übergeben und nach Wert zugewiesen. Bestimmte integrierte Typen, einschließlich Kanaltypen und Zuordnungstypen, verhalten sich als opake Zeiger auf eine verborgene interne Struktur. Und es ist möglich, diese interne Struktur durch Operationen auf dem Kanal oder der Karte zu modifizieren. Sie beginnen als nil, die analog zum Zeiger nil ist.

Verwandte Themen