Bir Go nesnesinin işaretçi değerini nasıl yazdırırım? İşaretçi değeri ne anlama geliyor?


83

Sadece Go ile oynuyorum ve yapıların değer veya referans ile ne zaman aktarıldığına dair iyi bir zihinsel modele sahip değilim.

Bu çok aptalca bir soru olabilir, ancak biraz denemek ve hala aynı nesne üzerinde çalışıp çalışmadığımı veya bir kopyasını oluşturduğumu (değerine göre geçti) görmek istiyorum.

Bir nesnenin işaretçisini (veya işaretçi değeri gc tarafından değiştirilirse dahili kimliği) yazdırmanın bir yolu var mı?

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

pencerelerimde verir (8g derlenmiş versiyonu):

0x4930d4
from gotest:
0x4974d8
42
0x4930d4
42

Go rutinindeki işaretçi değeri neden farklı bir değer gösteriyor? Orijinal nesnenin miktarı değiştiği için aynı nesneyle çalışıyordu. Kalıcı bir nesne kimliğini görmenin bir yolu var mı?

Yanıtlar:


116

Go işlevi bağımsız değişkenleri değer ile iletilir.

Öncelikle, bir argümanı yalnızca değere göre ilettiğinizi kolayca görebilmemiz için örneğinizin alakasız kısımlarını bir kenara atalım. Örneğin,

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

Çıktı:

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

Fonksiyonunda main, ibir bir intbellek konumunda bir değişken ( &i) 0xf800000040bir başlangıç değeri ile ( i) 42.

Fonksiyonda main, bir değere ( = ) işaret eden bir değere ( = ) sahip bellek konumundaki ( ) pbir intdeğişkene göstericidir .&p0xf8000000f0p&i0xf800000040int*pi42

Fonksiyonda main, byval(p)değeri (atar bir işlev çağrısı p= &i) 0xf800000040bellek konumu (en argüman &p) 0xf8000000f0fonksiyonu byvalparametre qbellek konumunda ( &q) 0xf8000000d8. Başka bir deyişle, byvalparametre için bellek ayrılır qve main byvalbağımsız değişkenin değeri pona atanır; değerleri pve qilk olarak aynıdır, ancak değişkenler pve qfarklıdır.

İşlevde byval, pointer q( *int) 'ın bir kopyası olan pointer p( *int) kullanılarak integer *q( i) yeni bir int değerine ayarlanır 4143. Dönmeden önce sonunda. işaretçi (sıfır değeri) qolarak ayarlanmıştır nil, bu bir kopya polduğundan üzerinde hiçbir etkisi yoktur q.

Fonksiyonunda main, pbir bir işaretçidir intbellek konumu (değişkenin &p) 0xf8000000f0değeri ile ( p= &i) 0xf800000040, yeni olduğu noktaları intdeğeri ( *p= i) 4143.

İşlevsel olarak main, bellek konumunda ( ) son değeri ( ) iolan bir intdeğişkendir .&i0xf800000040i4143

Örneğinizde, işlev çağrısına bağımsız maindeğişken olarak skullanılan işlev değişkeni gotest, işlev gotestparametresiyle aynı değildir s. Aynı ada sahiptirler, ancak farklı kapsamlara ve bellek konumlarına sahip farklı değişkenlerdir. İşlev parametresi s, işlev çağrısı bağımsız değişkenini gizler s. Bu yüzden örneğimde argüman ve parametre değişkenlerini adlandırdım pve qsırasıyla farkı vurgulamak için.

Örnekte, ( &s) 0x4930d4değişken için hafıza konumunun adresidir sişlevinde mainişlev çağrısı argüman olarak kullanılır gotest(s, done)ve 0x4974d8fonksiyon için hafıza konumunun adresidir gotestparametresi s. Parametreyi s = nilfonksiyonun sonunda ayarlarsanız gotest, değişken sin üzerinde etkisi olmaz main; siçinde mainve siçinde gotestfarklı bellek konumlarıdır. Türleri açısından, &solduğu **Something, solduğunu *Something, ve *sbir Something. &s(bellek konumu adresi) siçin bir göstericidir, bu bir işaretçi (bellek konumu adresi) bir tür anonim değişkenSomething. Değerler açısından, main.&s != gotest.&s, main.s == gotest.s, main.*s == gotest.*s, ve main.s.number == gotest.s.number.

MKB'nin bilgece tavsiyesini dinlemeli ve kullanmayı bırakmalısınız println(&s). fmtPaketi kullanın, örneğin,

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

İşaretçiler aynı bellek konumuna işaret ettiklerinde aynı değere sahiptir; işaretçiler, farklı bellek konumlarına işaret ettiklerinde farklı değerlere sahiptir.


Örneğimde gotest, 'Bir şey'e bir işaretçi alır, bu yüzden aynı nesneye atıfta bulunduğunu varsayıyorum ve açıkça, go-rutini içindeki değeri değiştirdikten sonra nesnenin de değerinin ana işlevde değişmiş olmasıdır. . Yazdırılan işaretçi değeri farklı.
Jeroen Dirks

@JamesDean Örneğinizde, işaretçi değeri & s türü yazdırıyorsunuz ** Bir şey, işaretçi değerinin türü ile aynı değildir * Bir şey. Değere göre bir gösterici geçmek için örneğimi revize ettim.
peterSO

@James Dean İşaretçinin adresini yazdırdınız (yani işaretçiye bir sişaretçi), - işaretçiler değere göre iletilir, adresi ile saynı değildir s. En baştaki fonksiyonunuz println( s )işe yaramazsa, işaretçi değerini yazdırır.
nos

Oh şimdi neler olduğunu anlıyorum. Println (& s) yaparak, işaretçi değeri yerine işaretçinin adresini yazdırıyordum. Println (s) 'i yapmış olsaydım, main ve go rutin fonksiyonunda aynı işaretçiyi gösterecekti.
Jeroen Dirks

@JamesDean Kesinlikle. C'deki gibi Go'da, s * Bir şey için, & s, s ve * s arasındaki farkı bilmek önemlidir.
peterSO

6

Go'da, bağımsız değişkenler değere göre iletilir.

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

Çıktı:

s: 0xf800000040 {1}
v: 0xf8000000e0 {1}
v: 0xf8000000e0 {2}
s: 0xf800000040 {1}
p: 0xf800000040 {1}
p: 0xf800000040 {2}
s: 0xf800000040 {2}

2
type sometype struct { }
a := sometype {}
b := int(2)
println("Ptr to a", &a)
println("Ptr to b", &b)


2

Bir Go nesnesinin işaretçi değerini nasıl yazdırırım ?

package main

import (
    "fmt"
)

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

sonuçlanır:

0x1040a124

İşaretçi değeri ne anlama geliyor?

Wikipedia'ya göre :

Bir işaretçi bellekteki bir konuma başvurur


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

ÇIKTI:

$ go run pointers.go
initial: 1
zeroval: 1
zeroptr: 0
pointer: 0x42131100
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.