Jak wydrukować wartość wskaźnika obiektu Go? Co oznacza wartość wskaźnika?

83

Po prostu bawię się w Go i nie mam jeszcze dobrego modelu myślowego, kiedy struktury są przekazywane przez wartość lub przez odniesienie.

To może być bardzo głupie pytanie, ale chcę po prostu trochę poeksperymentować i zobaczyć, czy nadal pracuję nad tym samym obiektem, czy też wykonałem jego kopię (przekazałem ją według wartości).

Czy istnieje sposób na wydrukowanie wskaźnika (lub wewnętrznego identyfikatora, jeśli wartość wskaźnika zostanie zmieniona przez gc) obiektu?

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

daje na moich oknach (wersja skompilowana 8g):

0x4930d4
from gotest:
0x4974d8
42
0x4930d4
42

Dlaczego wartość wskaźnika z procedury go pokazuje inną wartość? Ilość na oryginalnym obiekcie została zmieniona, więc działała z tym samym obiektem. Czy istnieje sposób, aby zobaczyć identyfikator obiektu, który jest trwały?

Jeroen Dirks
źródło

Odpowiedzi:

115

Argumenty funkcji Go są przekazywane przez wartość.

Po pierwsze, odrzućmy nieistotne części twojego przykładu, abyśmy mogli łatwo zobaczyć, że przekazujesz argument według wartości. Na przykład,

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

Wynik:

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

Funkcja main, ijest intzmienna w miejscu pamięci ( &i) 0xf800000040o początkowej wartości ( i) 42.

W zależności main, pto wskaźnik do intzmiennej w miejscu pamięci ( &p) 0xf8000000f0o wartości ( p= &i) 0xf800000040, który wskazuje na intwartość ( *p= i) 42.

W zależności main, byval(p)to wywołanie funkcji, która przypisuje wartości ( p= &i) 0xf800000040argumentu w komórce pamięci ( &p) 0xf8000000f0do funkcji byvalparametru qw miejscu pamięci ( &q) 0xf8000000d8. Innymi słowy, pamięć jest przydzielana dla byvalparametru qi przypisywana jest do niego wartość main byvalargumentu p; wartości pi qsą początkowo takie same, ale zmienne pi qsą różne.

W funkcji byval, używając pointer q( *int), który jest kopią pointer p( *int), integer *q( i) jest ustawiana na nową wartość int 4143. Na koniec przed powrotem. wskaźnik qjest ustawiony na nil(wartość zerowa), co nie ma wpływu na to, pże qjest kopią.

W zależności main, pto wskaźnik do intzmiennej w miejscu pamięci ( &p) 0xf8000000f0o wartości ( p= &i), 0xf800000040co wskazuje na nową intwartość ( *p= i) 4143.

Funkcja main, ijest intzmienna w miejscu pamięci ( &i) 0xf800000040o wartości końcowej ( i) 4143.

W naszym przykładzie mainzmienna funkcji sużywana jako argument gotestwywołania funkcji nie jest tym samym, co gotestparametr funkcji s. Mają tę samą nazwę, ale są różnymi zmiennymi z różnymi zakresami i lokalizacjami pamięci. Parametr funkcji sukrywa argument wywołania funkcji s. Dlatego w moim przykładzie nazwałem zmienne argumentu i parametru poraz qodpowiednio, aby podkreślić różnicę.

W naszym przykładzie ( &s) 0x4930d4jest adresem lokalizacji pamięci dla zmiennej sw funkcji, mainktóra jest używana jako argument wywołania funkcji gotest(s, done), i 0x4974d8jest adresem lokalizacji pamięci dla gotestparametru funkcji s. Jeśli ustawisz parametr s = nilna końcu funkcji gotest, nie będzie to miało wpływu na zmienną sin main; sin maini sin gotestto różne lokalizacje pamięci. Pod względem typów &sjest **Something, sjest *Somethingi *sjest Something. &sjest wskaźnikiem na (adres lokalizacji pamięci) s, który jest wskaźnikiem do (adres lokalizacji pamięci) anonimowej zmiennej typuSomething. Pod względem wartości, main.&s != gotest.&s, main.s == gotest.s, main.*s == gotest.*s, i main.s.number == gotest.s.number.

Powinieneś skorzystać z mądrej rady mkb i przestać używać println(&s). Skorzystaj z fmtpakietu, na przykład

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

Wskaźniki mają tę samą wartość, gdy wskazują to samo miejsce w pamięci; wskaźniki mają różne wartości, gdy wskazują różne lokalizacje pamięci.

peterSO
źródło
W moim przykładzie gotest pobiera wskaźnik do `` Coś '', więc zakładam, że odnosi się do tego samego obiektu i wyraźnie jest tak, ponieważ po zmianie wartości w procedurze go obiekt ma również zmienioną wartość w funkcji głównej . Wydrukowana wartość wskaźnika jest inna.
Jeroen Dirks
@JamesDean W swoim przykładzie wypisujesz wartość wskaźnika i typ s ** Coś, co nie jest tym samym, co typ wartości wskaźnika s * Coś. Poprawiłem mój przykład, aby przekazać wskaźnik według wartości.
peterSO
@James Dean Wydrukowałeś adres wskaźnika (tj. Wskaźnik do swskaźnika), - wskaźniki są przekazywane przez wartość, adres snie jest taki sam jak s. Jeśli twoja gotest funkcja println( s )intead, wypisuje wartość wskaźnika.
nr
Och, teraz widzę, co się dzieje. Robiąc println (& s), drukowałem adres wskaźnika zamiast wartości wskaźnika. Gdybym zrobił println (s), pokazałby ten sam wskaźnik w funkcji main i go.
Jeroen Dirks
@JamesDean Dokładnie. W Go jak w C, for s * Coś, ważne jest, aby znać różnicę między & s, s i * s.
peterSO
6

W Go argumenty są przekazywane według wartości.

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

Wynik:

s: 0xf800000040 {1}
v: 0xf8000000e0 {1}
v: 0xf8000000e0 {2}
s: 0xf800000040 {1}
p: 0xf800000040 {1}
p: 0xf800000040 {2}
s: 0xf800000040 {2}
peterSO
źródło
2
type sometype struct { }
a := sometype {}
b := int(2)
println("Ptr to a", &a)
println("Ptr to b", &b)

źródło
4
Nie powinieneś używać wbudowanego println, ale użyj czegoś odpowiedniego z pakietu fmt: golang.org/doc/go_spec.html#Bootstrapping
mkb
2

Jak wydrukować wartość wskaźnika obiektu Go?

package main

import (
    "fmt"
)

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

prowadzi do:

0x1040a124

Co oznacza wartość wskaźnika?

Według Wikipedii :

Wskaźnik odnosi się do lokalizacji w pamięci

030
źródło
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)
}

WYNIK:

$ go run pointers.go
initial: 1
zeroval: 1
zeroptr: 0
pointer: 0x42131100
Jetlum Ajeti
źródło