2016-04-14 13 views
0

Ich habe lange keine Zeiger mehr benutzt, und als ich es tat, war es nur in einer akademischen Umgebung und ich bin ein bisschen eingerostet, dank C#/Java/Scala.Häufige Fehler mit Zeigern in Go

Was sind einige häufige Fehler, die Leute mit Zeigern in Golang machen?

Gibt es Möglichkeiten zu testen, wenn Sie sie richtig verwendet haben? Ich denke, es ist immer schwer, ein Speicherleck zu erkennen, bis etwas schief läuft.

+1

Go's noch Müll gesammelt. Die einzige Sache, um die Sie sich wirklich kümmern müssen, ist die korrekte Dereferenzierung in den seltenen Fällen, in denen Sie müssen. Der Compiler prüft dies implizit, weil Sie inkompatible Typen haben, wenn sie falsch sind.Die andere Sache, die dazu tendiert, Leute zu versauen, ist Zeiger-Wert-Empfänger auf Methoden. Wenn Ihr Empfänger ein Wert ist, wird er als Wert an die Methode übergeben. Wenn es ein Zeiger ist, dann wird dies per Referenz getan. Wenn Sie also den Zustand eines Objekts innerhalb einer empfangenen Methode ändern möchten, müssen Sie einen Zeiger verwenden. – evanmcdonnal

Antwort

3

Da Go Müll gesammelt ist und keine Zeigerarithmetik erlaubt, gibt es nicht viel, was man falsch machen kann. Sie können unsafe Paket dafür verwenden, aber es spricht für sich selbst - es ist unsicher.

nil Zeiger sind immer noch da. Die Dereferenzierung von ihnen verursacht eine Panik, die in C#/Java etwas wie Ausnahmen ist - Sie erhalten eine klare Fehlerbeschreibung und eine Stapelverfolgung, wo es passiert ist.

Speicherlecks - GC wird fast alles für Sie tun, genau wie in C#/Java. Aber es gibt einen speziellen Fall, den ich kenne - Scheiben. ein Element zu entfernen ist in der Regel, indem noch ein Stück wie das getan:

a = append(a[:i], a[i+1:]...) 

dieser Code könnte das Element Leck Sie entfernt. Das liegt daran, dass intern slice eine Struktur ist, die ein Array (nur einen Zeiger), Länge und Kapazität enthält. Wenn Sie ein Element entfernen, enthält ein neues Segment möglicherweise dasselbe Array und verweist weiterhin auf das Element, das Sie entfernt haben. GC wird es nicht freigeben. Um das zu lösen, müssen Sie das Element nichtig machen, bevor Sie es entfernen.

Und es gibt auch Zeiger vs Wert Methode Empfänger Verwirrung. Es ist kein Fehler, eher wie eine Designentscheidung, die Sie treffen und verstehen müssen. Methode mit Wert-Empfänger wird eine Kopie des Empfängers erhalten, es kann den Zustand nicht ändern. Wenn Sie also den Zustand ändern möchten, benötigen Sie einen Zeigerempfänger. Auch wenn Ihre Strukturen groß sind und Sie nicht möchten, dass sie jedes Mal kopiert werden, wenn Sie ein Methid aufrufen, können Sie auch Zeigerempfänger verwenden.

0

Der häufige Fehler, den ich gefunden habe, ist, dass Leute Zeiger in ihren komplizierten Strukturen vergessen.

Jede Scheibe, Karte, Schnittstelle ist ein Zeiger. Wenn Sie eine Struktur haben, die eine andere Struktur enthält, die einen Zeiger enthält, sehen Sie vielleicht S1{s2: S2}, und Sie denken, es ist in Ordnung, eine Struktur wie folgt zu kopieren: a=b, wenn es eigentlich nicht in Ordnung ist, wie in S2, ein poiner vatriable, sagen wir p , wird ihre Adresse kopiert und nicht der Wert, auf den sie zeigt. Wenn Sie den Wert von * a.s2.p ändern, gibt der * b.s2.p den gleichen Wert zurück.

package main 

import (
    "fmt" 
) 

type S1 struct { 
    v int 
    s2 S2 
} 

type S2 struct { 
    p *int 
} 

func main() { 
    x := 1 
    b := S1{v: 10, s2: S2{p: &x}} 
    a := b 
    fmt.Printf("a = %+v\nb = %+v\n*a.s2.p = %d\n*b.s2.p = %d\na.s2.p = %p\nb.s2.p = %p\n", a, b, *a.s2.p, *b.s2.p, a.s2.p, b.s2.p) 
    *a.s2.p = 5 
    fmt.Printf("*a.s2.p = %d\n*b.s2.p = %d\na.s2.p = %p\nb.s2.p = %p\n", *a.s2.p, *b.s2.p, a.s2.p, b.s2.p) 
} 

http://play.golang.org/p/KQ99KICgbu

Dieses es ist ein sehr einfaches Beispiel, und es sieht offensichtlich gibt es ein Problem, aber bei größeren Anwendungen könnte dies nicht so offensichtlich sein.

Dieses Problem tritt auch bei Kanälen auf. Wenn Sie einen Zeiger senden, erhalten Sie am anderen Ende den gleichen Zeiger (eine Kopie des Werts Ihres Zeigers, der eine Adresse ist). In diesem Fall, wie im ersten Fall, besteht eine sichere Lösung darin, eine Clone-Funktion zu verwenden, um ein geklontes Objekt zu erzeugen. Sie senden den Klon über Ihren Kanal und verwenden ihn nicht mehr auf der Senderseite.