Je suis une programmation newbee to go. J'ai lu dans le livre de programmation aller que cette tranche se compose de trois choses: un pointeur sur un tableau, la longueur et la capacité.
Je suis confus entre tranches nulles (la tranche n'a pas de tableau sous-jacent, len = 0, cap = 0), des tranches non nuls où seul len = 0, cap = 0 et des tranches vides.
Quelqu'un peut-il dire s'il vous plaît si les tranches nulles et vides sont identiques? Si les deux sont différents, alors s'il vous plaît dites quelle est la différence entre ces deux?
Comment tester si une tranche est vide ou non?
En outre, quelle est la valeur du pointeur dans les tranches non nulles, dont la longueur et la capacité sont égales à zéro?
nil
et les tranches vides (avec une capacité de 0) ne sont pas identiques, mais leur comportement observable est identique. Je veux dire par là:
len()
et cap()
for range
dessus (ce sera 0 itérations)Voir cet exemple simple (une tranche nil
et 2 tranches vides non -nil
):
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 {}
Sortie (essayez-le sur Go Playground ):
s1 0 0 true [] true
s2 0 0 false [] false
s3 0 0 false [] false
(Notez que le découpage d'une tranche nil
donne une tranche nil
, tandis que le découpage d'une tranche non -nil
donne une tranche non -nil
.)
Vous ne pouvez faire la différence qu'en comparant la valeur de la tranche à l'identificateur prédéclaré nil
, ils se comportent de la même manière dans tous les autres aspects.
Pour savoir si une tranche est vide, comparez simplement sa longueur à 0
: len(s) == 0
. Peu importe qu'il s'agisse de la tranche nil
ou d'une tranche non -nil
, il importe également si sa capacité est positive; s'il n'y a pas d'éléments, c'est vide.
s := make([]int, 0, 100)
fmt.Println("Empty:", len(s) == 0, ", but capacity:", cap(s))
Imprime (essayez sur le Go Playground ):
Empty: true , but capacity: 100
Une valeur de tranche est représentée par une structure définie dans reflect.SliceHeader
:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
Dans le cas d'une tranche nil
, cette structure aura sa valeur zéro, c'est-à-dire que tous ses champs auront leur valeur zéro, c'est-à-dire: 0
.
Avoir une tranche non -nil
avec à la fois une capacité et une longueur égales aux champs 0
, Len
et Cap
sera très certainement 0
, mais le pointeur Data
pourrait ne pas l'être. Ce n'est pas {sera}, c'est ce qui le différencie de la tranche nil
. Cela pointera vers un tableau sous-jacent de taille zéro.
Notez que la spécification Go permet aux valeurs de différents types ayant une taille égale à 0 d'avoir la même adresse mémoire. Spécifications: Considérations relatives au système: Dimensions et garanties d'alignement:
Un type de structure ou de tableau a la taille zéro s'il ne contient aucun champ (ou élément, respectivement) de taille supérieure à zéro. Deux variables de taille zéro distinctes peuvent avoir la même adresse en mémoire.
Vérifions cela. Pour cela, nous appelons l'aide de unsafe
package et "obtenons" la structure reflect.SliceHeader
"vue" de nos valeurs de tranche:
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)))
Sortie (essayez-le sur 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}
Que voit-on?
nil
a le pointeur de données 0
s2
et s3
ont le même pointeur de données, partageant/pointant vers la même valeur de mémoire de taille 0 Une tranche peut être nulle dans un sens très littéral:
var n []int
n == nil // true
C'est le seul cas facile. La notion de tranche "vide" n'est pas bien définie: une tranche s
avec len(s) == 0
est certainement vide, quelle que soit sa capacité . La chose la plus sensée à faire est la suivante: Ignorer l'implémentation sous-jacente, on n'a jamais besoin de savoir comment une tranche est représenté en interne. Tout ce qui compte est le comportement défini d'une tranche.
Comment tester si une tranche est vide ou non?
La définition la plus sensée de "slice s
is empty" est une tranche ne contenant aucun élément et se traduisant par len(s) == 0
. Cette définition est valable pour les tranches nuls et non nuls.
Quelqu'un peut-il dire s'il vous plaît si les tranches nulles et vides sont identiques? Si les deux sont différents, alors s'il vous plaît dites quelle est la différence entre ces deux?
Techniquement, une tranche nulle et une tranche non nulle sont différentes (l'une étant == nulle, l'autre étant! = Nulle), mais cette distinction n'a généralement pas d'importance, car vous pouvez append
passer d'une valeur nulle à une valeur nulle et inversement. 0
var n []int
len(n) == cap(n) == 0 // true
n = append(n, 123)
len(n) == 1 // true
Lisez à propos de zéro dans Go pour plus d’informations. Une tranche nulle est comme un canal nul ou une carte nulle: elle est non initialisée. Vous initialisez soit par make
ing, soit par un littéral. Comme indiqué ci-dessus, il n'y a aucune raison de penser à la représentation sous-jacente.
En outre, quelle est la valeur du pointeur dans les tranches non nulles, dont la longueur et la capacité sont nulles?
Il s'agit d'un détail d'implémentation qui peut varier d'un compilateur à l'autre ou même d'une version à l'autre. Personne n'a besoin de le savoir pour écrire des programmes Go corrects et portables.