Ich bin ein Neuling, um zu programmieren. Ich habe im go-Programmierbuch gelesen, dass Slice aus drei Dingen besteht: einem Zeiger auf ein Array, Länge und Kapazität.
Ich werde verwirrt zwischen null Slices (Slice hat kein darunter liegendes Array, auf das gezeigt werden soll, len = 0, cap = 0), nicht-null Slices, wobei nur len = 0, cap = 0 und leere Slices sind.
Kann jemand bitte sagen, ob null und leere Scheiben gleich sind? Wenn beide unterschiedlich sind, dann sagen Sie bitte, was ist der Unterschied zwischen diesen beiden?
Wie kann man testen, ob ein Slice leer ist oder nicht?
Welchen Wert hat der Zeiger auch in Nicht-Null-Schnitten, deren Länge und Kapazität Null sind?
nil
und leere Slices (mit 0 Kapazität) sind nicht dasselbe, aber ihr beobachtbares Verhalten ist dasselbe. Damit meine ich:
len()
und cap()
übergebenfor range
Über sie (wird 0 Iterationen sein)Sehen Sie sich dieses einfache Beispiel an (ein nil
- Slice und 2 nicht - nil
leere Slices):
var s1 []int // nil slice
s2 := []int{} // non-nil, empty slice
s3 := make([]int, 0) // non-nil, empty slice
fmt.Println("s1", len(s1), cap(s1), s1 == nil, s1[:], s1[:] == nil)
fmt.Println("s2", len(s2), cap(s2), s2 == nil, s2[:], s2[:] == nil)
fmt.Println("s3", len(s3), cap(s3), s3 == nil, s3[:], s3[:] == nil)
for range s1 {}
for range s2 {}
for range s3 {}
Ausgabe (versuchen Sie es auf dem Go Playground ):
s1 0 0 true [] true
s2 0 0 false [] false
s3 0 0 false [] false
(Beachten Sie, dass das Schneiden eines Slice nil
zu einem Slice nil
und das Schneiden eines Slice ohne nil
zu einem Slice ohne nil
führt.)
Sie können den Unterschied nur feststellen, indem Sie den Slice-Wert mit dem vordeklarierten Bezeichner nil
vergleichen. Sie verhalten sich in allen anderen Aspekten gleich.
Um festzustellen, ob ein Slice leer ist, vergleichen Sie einfach seine Länge mit 0
: len(s) == 0
. Es spielt keine Rolle, ob es sich um den Slice nil
oder einen Slice ohne nil
handelt. Es spielt auch keine Rolle, ob er eine positive Kapazität hat. Wenn es keine Elemente enthält, ist es leer.
s := make([]int, 0, 100)
fmt.Println("Empty:", len(s) == 0, ", but capacity:", cap(s))
Ausdrucke (versuchen Sie es auf dem Go Playground ):
Empty: true , but capacity: 100
Ein Slice-Wert wird durch eine Struktur dargestellt, die in reflect.SliceHeader
definiert ist:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
Im Fall eines nil
- Slice hat diese Struktur den Wert Null, dh alle ihre Felder haben den Wert Null, dh 0
.
Wenn ein Nicht-Slice mit einer Kapazität und Länge von nil
gleich 0
, Len
und Cap
ist, ist dies mit Sicherheit 0
. aber der Data
Zeiger kann nicht sein. Es wird nicht sein, das ist es, was es von der Scheibe nil
unterscheidet. Es zeigt auf ein zugrunde liegendes Array der Größe Null.
Beachten Sie, dass die Go-Spezifikation Werte verschiedener Typen mit der Größe 0 für dieselbe Speicheradresse zulässt. Spezifikation: Systemüberlegungen: Garantie für Größe und Ausrichtung:
Ein Struktur- oder Array-Typ hat die Größe Null, wenn er keine Felder (oder Elemente) enthält, deren Größe größer als Null ist. Zwei unterschiedliche Variablen mit der Größe Null haben möglicherweise dieselbe Adresse im Speicher.
Lassen Sie uns das überprüfen. Dazu rufen wir die Hilfe des Pakets unsafe
auf und "holen" uns die Strukturansicht reflect.SliceHeader
Unserer Slice-Werte:
var s1 []int
s2 := []int{}
s3 := make([]int, 0)
fmt.Printf("s1 (addr: %p): %+8v\n",
&s1, *(*reflect.SliceHeader)(unsafe.Pointer(&s1)))
fmt.Printf("s2 (addr: %p): %+8v\n",
&s2, *(*reflect.SliceHeader)(unsafe.Pointer(&s2)))
fmt.Printf("s3 (addr: %p): %+8v\n",
&s3, *(*reflect.SliceHeader)(unsafe.Pointer(&s3)))
Ausgabe (versuchen Sie es auf dem Go Playground ):
s1 (addr: 0x1040a130): {Data: 0 Len: 0 Cap: 0}
s2 (addr: 0x1040a140): {Data: 1535812 Len: 0 Cap: 0}
s3 (addr: 0x1040a150): {Data: 1535812 Len: 0 Cap: 0}
Was sehen wir?
nil
hat einen Datenzeiger 0
s2
Und s3
Haben denselben Datenzeiger, der auf denselben Speicherwert der Größe 0 verweistEin Slice kann im wahrsten Sinne des Wortes Null sein:
var n []int
n == nil // true
Das ist der einzig einfache Fall. Der Begriff "leere" Slice ist nicht klar definiert: Eine Slice s
mit len(s) == 0
ist auf jeden Fall leer, unabhängig von ihrer Kapazität. Das Vernünftigste ist, die zugrunde liegende Implementierung zu ignorieren ist intern vertreten. Alles was zählt ist das definierte Verhalten eines Slice.
Wie kann man testen, ob ein Slice leer ist oder nicht?
Die sinnvollste Definition von "Slice s
ist leer" ist ein Slice, das keine Elemente enthält, die in len(s) == 0
übersetzt werden. Diese Definition gilt sowohl für Null als auch für Nicht-Null-Scheiben.
Kann jemand bitte sagen, ob null und leere Scheiben gleich sind? Wenn beide unterschiedlich sind, dann sagen Sie bitte, was ist der Unterschied zwischen diesen beiden?
Technisch gesehen sind eine Null-Scheibe und eine Nicht-Null-Scheibe unterschiedlich (eine ist == Null, die andere ist! = Null), aber diese Unterscheidung spielt normalerweise keine Rolle, da Sie append
eine Null-Scheibe und keine Null-Schicht zurücklegen können 0
var n []int
len(n) == cap(n) == 0 // true
n = append(n, 123)
len(n) == 1 // true
Weitere Informationen finden Sie in Go über die Nullwerte. Ein Nullteil ist wie ein Nullkanal oder eine Nullkarte: Es ist nicht initialisiert. Sie initialisieren sich entweder durch make
ing oder durch ein Literal. Wie oben erwähnt, gibt es keinen Grund, über die zugrunde liegende Darstellung nachzudenken.
Welchen Wert hat der Zeiger auch in Nicht-Null-Schnitten, deren Länge und Kapazität Null sind?
Dies ist ein Implementierungsdetail, das von Compiler zu Compiler oder sogar von Version zu Version variieren kann. Niemand muss dies wissen, um korrekte und tragbare Go-Programme zu schreiben.