Pourquoi utiliser la sémantique du pointeur dans l'itérateur de boucle aboutit à référencer la même valeur? [dupliquer]
Un exemple simplifié illustrant un cas de buggy rencontré avec plusieurs couches:
// Store a list of boxed integers in map and print them.
// This prints 3 times the int value of 3.
type number struct{ val int }
func setInMap() map[int]*number {
list := []number{{1}, {2}, {3}}
m := make(map[int]*number)
for i, n := range list {
// when saving the address of n,
// all values point -> 3
m[i] = &n
}
return m
}
func main() {
m := setInMap()
for _, n := range m {
// Prints 3,3,3 or last set value when pointer
// instead of 1,2,3
fmt.Print(n.val, ",")
}
}
Buggy Pointer Semantics Playground lien
Comparez cela avec l'enregistrement d'une version sémantique de valeur qui fonctionne correctement:
for i, n := range list {
// set -> map using value semantics
// prints all values
m[i] = n
}
Lien Value Semantics Playground
Réponses
La raison en est que la for..range
construction utilise la sémantique des valeurs, ce qui fait que le compilateur copie chaque valeur de liste dans la pile , c'est-à-dire le contenu de int n
.
// n is copied into the stack at each iteration
for i, n := range list
Plus tard, en prenant l'adresse de n, nous stockons l'adresse de la même variable itérée de pile.
// when saving the address of n,
// in a for..range loop we are taking the address of
// same iterated variable n in stack.
// resulting in the same value stored thrice in the map.
m[i] = &n
Pour corriger la version sémantique du pointeur, utilisez for i=
la version correcte suivante :
// n is now pointing to distinct
// members of the list in heap.
for i := 0; i < len(list); i++ {
n := &list[i]
m[i] = n
}
En guise de note de performance supplémentaire, cela améliore l' efficacité , ce qui est important pour les n objets volumineux, car cela supprime la copie inefficace et impartiale de n objets contenant des objets hiérarchiques imbriqués.
Playground (version correcte)