Je ne fais que jouer avec Go et je n'ai pas encore un bon modèle mental du moment où les structs sont passés par valeur ou par référence.
C'est peut-être une question très stupide mais je veux juste expérimenter un peu et voir si je travaille toujours sur le même objet ou si j'en ai fait une copie (passée par valeur).
Est-il possible d'imprimer le pointeur (ou id interne si la valeur du pointeur est modifiée par gc) d'un objet?
package main
import ( "runtime" )
type Something struct {
number int
queue chan int
}
func gotest( s *Something, done chan bool ) {
println( "from gotest:")
println( &s )
for num := range s.queue {
println( num )
s.number = num
}
done <- true
}
func main() {
runtime.GOMAXPROCS(4)
s := new(Something)
println(&s)
s.queue = make(chan int)
done := make(chan bool)
go gotest(s, done)
s.queue <- 42
close(s.queue)
<- done
println(&s)
println(s.number)
}
donne sur mes fenêtres (version compilée 8g):
0x4930d4
from gotest:
0x4974d8
42
0x4930d4
42
Pourquoi la valeur du pointeur dans la routine aller affiche-t-elle une valeur différente? La quantité sur l'objet d'origine a été modifiée pour qu'il fonctionne avec le même objet. Existe-t-il un moyen de voir un identifiant d'objet persistant?
Les arguments de la fonction Go sont passés par valeur.
Commençons par supprimer les parties non pertinentes de votre exemple afin que nous puissions facilement voir que vous ne faites que passer un argument par valeur. Par exemple,
package main
import "fmt"
func byval(q *int) {
fmt.Printf("3. byval -- q %T: &q=%p q=&i=%p *q=i=%v\n", q, &q, q, *q)
*q = 4143
fmt.Printf("4. byval -- q %T: &q=%p q=&i=%p *q=i=%v\n", q, &q, q, *q)
q = nil
}
func main() {
i := int(42)
fmt.Printf("1. main -- i %T: &i=%p i=%v\n", i, &i, i)
p := &i
fmt.Printf("2. main -- p %T: &p=%p p=&i=%p *p=i=%v\n", p, &p, p, *p)
byval(p)
fmt.Printf("5. main -- p %T: &p=%p p=&i=%p *p=i=%v\n", p, &p, p, *p)
fmt.Printf("6. main -- i %T: &i=%p i=%v\n", i, &i, i)
}
Sortie:
1. main -- i int: &i=0xf840000040 i=42
2. main -- p *int: &p=0xf8400000f0 p=&i=0xf840000040 *p=i=42
3. byval -- q *int: &q=0xf8400000d8 q=&i=0xf840000040 *q=i=42
4. byval -- q *int: &q=0xf8400000d8 q=&i=0xf840000040 *q=i=4143
5. main -- p *int: &p=0xf8400000f0 p=&i=0xf840000040 *p=i=4143
6. main -- i int: &i=0xf840000040 i=4143
Dans la fonction main
, i
est une variable int
située à l'emplacement mémoire (&i
) 0xf800000040
avec une valeur initiale (i
) 42
.
Dans la fonction main
, p
est un pointeur sur une variable int
à l'emplacement mémoire (&p
) 0xf8000000f0
avec une valeur (p
= &i
) 0xf800000040
qui pointe vers une valeur int
(*p
= i
) 42
.
Dans la fonction main
, byval(p)
est un appel de fonction qui attribue la valeur (p
= &i
) 0xf800000040
de l'argument situé à l'emplacement mémoire (&p
) 0xf8000000f0
à la fonction byval
paramètre q
à l'emplacement mémoire (&q
) 0xf8000000d8
. En d'autres termes, la mémoire est allouée pour le paramètre byval
q
et la valeur de l'argument main
byval
p
lui est affectée. les valeurs de p
et q
sont initialement les mêmes, mais les variables p
et q
sont distinctes.
Dans la fonction byval
, à l'aide du pointeur q
(*int
), qui est une copie du pointeur p
(*int
), l'entier *q
(i
) est défini sur une nouvelle valeur int 4143
. À la fin avant de revenir. le pointeur q
est défini sur nil
(valeur zéro), ce qui n'a aucun effet sur p
puisque q
est une copie.
Dans la fonction main
, p
est un pointeur sur une variable int
à l'emplacement mémoire (&p
) 0xf8000000f0
avec une valeur (p
= &i
) 0xf800000040
qui pointe vers une nouvelle valeur int
(*p
= i
) 4143
.
Dans la fonction main
, i
est une variable int
située à l'emplacement mémoire (&i
) 0xf800000040
avec une valeur finale (i
) 4143
.
Dans votre exemple, la fonction main
variable s
utilisée comme argument de l'appel de la fonction gotest
n'est pas la même que celle de la fonction gotest
paramètre s
. Elles portent le même nom, mais sont des variables différentes avec des étendues et des emplacements de mémoire différents. Le paramètre de fonction s
masque l'argument d'appel de fonction s
. C'est pourquoi, dans mon exemple, j'ai nommé l'argument et les variables de paramètre p
et q
respectivement pour souligner la différence.
Dans votre exemple, (&s
) 0x4930d4
est l'adresse de l'emplacement mémoire de la variable s
dans la fonction main
utilisée comme argument de l'appel de fonction gotest(s, done)
, et 0x4974d8
est l'adresse de l'emplacement mémoire de la fonction gotest
paramètre s
. Si vous définissez le paramètre s = nil
à la fin de la fonction gotest
, cela n'a aucun effet sur la variable s
dans main
; s
in main
et s
in gotest
sont des emplacements mémoire distincts. En termes de types, &s
est **Something
, s
est *Something
et *s
est Something
. &s
est un pointeur sur (adresse de l'emplacement mémoire) s
, qui est un pointeur sur (adresse de l'emplacement mémoire) une variable anonyme de type Something
. En termes de valeurs, main.&s != gotest.&s
, main.s == gotest.s
, main.*s == gotest.*s
et main.s.number == gotest.s.number
.
Vous devez suivre les conseils avisés de mkb et cesser d'utiliser println(&s)
. Utilisez le package fmt
, par exemple,
fmt.Printf("%v %p %v\n", &s, s, *s)
Les pointeurs ont la même valeur quand ils pointent vers le même emplacement mémoire. les pointeurs ont des valeurs différentes quand ils pointent vers des emplacements de mémoire différents.
Dans Go, les arguments sont passés par valeur.
package main
import "fmt"
type SomeStruct struct {
e int
}
// struct passed by value
func v(v SomeStruct) {
fmt.Printf("v: %p %v\n", &v, v)
v.e = 2
fmt.Printf("v: %p %v\n", &v, v)
}
// pointer to struct passed by value
func p(p *SomeStruct) {
fmt.Printf("p: %p %v\n", p, *p)
p.e = 2
fmt.Printf("p: %p %v\n", p, *p)
}
func main() {
var s SomeStruct
s.e = 1
fmt.Printf("s: %p %v\n", &s, s)
v(s)
fmt.Printf("s: %p %v\n", &s, s)
p(&s)
fmt.Printf("s: %p %v\n", &s, s)
}
Sortie:
s: 0xf800000040 {1}
v: 0xf8000000e0 {1}
v: 0xf8000000e0 {2}
s: 0xf800000040 {1}
p: 0xf800000040 {1}
p: 0xf800000040 {2}
s: 0xf800000040 {2}
package main
import "fmt"
func zeroval(ival int) {
ival = 0
}
func zeroptr(iptr *int) {
*iptr = 0
}
func main() {
i := 1
fmt.Println("initial:", i)
zeroval(i)
fmt.Println("zeroval:", i)
//The &i syntax gives the memory address of i, i.e. a pointer to i.
zeroptr(&i)
fmt.Println("zeroptr:", i)
//Pointers can be printed too.
fmt.Println("pointer:", &i)
}
SORTIE:
$ go run pointers.go
initial: 1
zeroval: 1
zeroptr: 0
pointer: 0x42131100
type sometype struct { }
a := sometype {}
b := int(2)
println("Ptr to a", &a)
println("Ptr to b", &b)