¿Cómo imprimo el valor del puntero de un objeto Go? ¿Qué significa el valor del puntero?

83

Estoy jugando con Go y todavía no tengo un buen modelo mental de cuándo las estructuras se pasan por valor o por referencia.

Esta puede ser una pregunta muy tonta, pero solo quiero experimentar un poco y ver si todavía estoy trabajando en el mismo objeto o si he hecho una copia (la pasé por valor).

¿Hay alguna forma de imprimir el puntero (o la identificación interna si gc cambia el valor del puntero) de un objeto?

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)
}

da en mi Windows (versión compilada 8g):

0x4930d4
from gotest:
0x4974d8
42
0x4930d4
42

¿Por qué el valor del puntero dentro de la rutina go muestra un valor diferente? La cantidad en el objeto original cambió, por lo que estaba trabajando con el mismo objeto. ¿Hay alguna forma de ver una identificación de objeto que sea persistente?

Jeroen Dirks
fuente

Respuestas:

114

Los argumentos de la función Go se pasan por valor.

Primero, descartemos las partes irrelevantes de su ejemplo, para que podamos ver fácilmente que simplemente está pasando un argumento por valor. Por ejemplo,

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)
}

Salida:

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

En función main, ies una intvariable en la ubicación de la memoria ( &i) 0xf800000040con un valor inicial ( i) 42.

En función main, pes un puntero a una intvariable en la ubicación de la memoria ( &p) 0xf8000000f0con un valor ( p= &i) 0xf800000040que apunta a un intvalor ( *p= i) 42.

En función main, byval(p)es una llamada de función que asigna el valor ( p= &i) 0xf800000040del argumento en la ubicación de la memoria ( &p) 0xf8000000f0al byvalparámetro de la función qen la ubicación de la memoria ( &q) 0xf8000000d8. En otras palabras, se asigna memoria para el byvalparámetro qy se le asigna el valor del main byvalargumento p; los valores de py qson inicialmente los mismos, pero las variables py qson distintas.

En función byval, usando pointer q( *int), que es una copia de pointer p( *int), integer *q( i) se establece en un nuevo valor int 4143. Al final antes de regresar. el puntero qse establece en nil(valor cero), lo que no tiene ningún efecto sobre pya que qes una copia.

En función main, pes un puntero a una intvariable en la ubicación de la memoria ( &p) 0xf8000000f0con un valor ( p= &i) 0xf800000040que apunta a un nuevo intvalor ( *p= i) 4143.

En función main, ies una intvariable en la ubicación de la memoria ( &i) 0xf800000040con un valor final ( i) 4143.

En su ejemplo, la mainvariable de función sutilizada como argumento para la gotestllamada a la función no es la misma que el gotestparámetro de la función s. Tienen el mismo nombre, pero son variables diferentes con diferentes ámbitos y ubicaciones de memoria. El parámetro de función soculta el argumento de llamada a la función s. Es por eso que en mi ejemplo, nombré las variables de argumento y parámetro py qrespectivamente para enfatizar la diferencia.

En su ejemplo, ( &s) 0x4930d4es la dirección de la ubicación de la memoria para la variable sen la función mainque se usa como argumento para la llamada a la función gotest(s, done), y 0x4974d8es la dirección de la ubicación de la memoria para el gotestparámetro de la función s. Si establece el parámetro s = nilal final de la función gotest, no tiene ningún efecto en la variable sen main; sen mainy sen gotestson ubicaciones de memoria distintas. En términos de tipos, &ses **Something, ses *Somethingy *ses Something. &ses un puntero a (dirección de ubicación de memoria) s, que es un puntero a (dirección de ubicación de memoria) una variable anónima de tipoSomething. En cuanto a los valores, main.&s != gotest.&s, main.s == gotest.s, main.*s == gotest.*s, y main.s.number == gotest.s.number.

Debe seguir el sabio consejo de mkb y dejar de usarlo println(&s). Utilice el fmtpaquete, por ejemplo,

fmt.Printf("%v %p %v\n", &s, s, *s)

Los punteros tienen el mismo valor cuando apuntan a la misma ubicación de memoria; Los punteros tienen valores diferentes cuando apuntan a diferentes ubicaciones de memoria.

peterSO
fuente
En mi ejemplo, el gotest toma un puntero a 'Algo', así que supongo que se refiere al mismo objeto y claramente lo es, ya que después de cambiar el valor dentro de la rutina go, el objeto también cambia su valor en la función principal . El valor del puntero impreso es diferente.
Jeroen Dirks
@JamesDean En su ejemplo, está imprimiendo el valor del puntero y el tipo ** Algo, que no es lo mismo que el tipo del valor del puntero * Algo. Revisé mi ejemplo para pasar un puntero por valor.
peterSO
@James Dean Imprimiste la dirección del puntero (es decir, un puntero al spuntero), - los punteros se pasan por valor, la dirección de sno es la misma que s. Si su función más obtenida lo hizo println( s ), imprimirá el valor del puntero.
nos
Oh, ahora veo lo que está pasando. Al hacer println (& s) estaba imprimiendo la dirección del puntero en lugar del valor del puntero. Si hubiera hecho println (s), habría mostrado el mismo puntero en la función de rutina main y go.
Jeroen Dirks
@JamesDean Exactamente. En Go como en C, para s * Something, es fundamental conocer la diferencia entre & s, sy * s.
peterSO
6

En Go, los argumentos se pasan por valor.

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)
}

Salida:

s: 0xf800000040 {1}
v: 0xf8000000e0 {1}
v: 0xf8000000e0 {2}
s: 0xf800000040 {1}
p: 0xf800000040 {1}
p: 0xf800000040 {2}
s: 0xf800000040 {2}
peterSO
fuente
2
type sometype struct { }
a := sometype {}
b := int(2)
println("Ptr to a", &a)
println("Ptr to b", &b)

fuente
4
No debe usar println incorporado, pero use algo apropiado del paquete fmt
mkb
2

¿Cómo imprimo el valor del puntero de un objeto Go?

package main

import (
    "fmt"
)

func main() {
    a := 42
    fmt.Println(&a)
}

resulta en:

0x1040a124

¿Qué significa el valor del puntero?

Según Wikipedia :

Un puntero hace referencia a una ubicación en la memoria

030
fuente
1
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)
}

SALIDA:

$ go run pointers.go
initial: 1
zeroval: 1
zeroptr: 0
pointer: 0x42131100
Jetlum Ajeti
fuente