Goオブジェクトのポインタ値を出力するにはどうすればよいですか?ポインタ値はどういう意味ですか?


83

私はGoで遊んでいるだけで、構造体が値または参照によって渡されるときの優れたメンタルモデルをまだ持っていません。

これは非常にばかげた質問かもしれませんが、少し実験して、同じオブジェクトで作業しているのか、それともコピーを作成したのか(値で渡したのか)を確認したいと思います。

オブジェクトのポインター(またはポインター値がgcによって変更された場合は内部ID)を出力する方法はありますか?

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

私のウィンドウに与える(8gコンパイルされたバージョン):

0x4930d4
from gotest:
0x4974d8
42
0x4930d4
42

goルーチン内のポインタ値が異なる値を示すのはなぜですか?元のオブジェクトの数量が変更されたため、同じオブジェクトで機能していました。永続的なオブジェクトIDを確認する方法はありますか?

回答:


114

Go関数の引数は値で渡されます。

まず、例の無関係な部分を破棄して、引数を値で渡しているだけであることが簡単にわかるようにします。例えば、

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

出力:

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

関数mainiは、は初期値()を持つintメモリ位置(&i)の変数です。0xf800000040i42

機能でmainpへのポインタであるintメモリ位置に変数(&p0xf8000000f0値(p= &i0xf800000040にどのポイントint値(*p= i42

関数でmainbyval(p)値を代入する関数呼び出しである(p= &i0xf800000040メモリ位置における引数の(&p0xf8000000f0関数のbyvalパラメータは、q(メモリ位置&q0xf8000000d8。つまり、byvalパラメータにメモリが割り当てられqmain byval引数の値がパラメータに割り当てられますp。値pq、最初は同じですが、変数pq別個のものです。

機能ではbyval、ポインタ使用q*intポインタのコピーである)、 (p)、*int整数*qi)新しいint値に設定されています4143。最後に戻る前に。ポインタqnil(ゼロ値)に設定されますが、これはコピーであるpため影響はありませんq

関数mainpは、は、新しい値(= )を指す値(= )を持つintメモリ位置(&p)の変数へのポインタです。0xf8000000f0p&i0xf800000040int*pi4143

関数mainiは、intはメモリ位置(&i)にある変数で0xf800000040あり、最終値は(i4143です。

あなたの例では、関数呼び出しの引数として使用される関数main変数は、関数パラメーターと同じではありません。それらは同じ名前ですが、スコープとメモリ位置が異なる異なる変数です。関数パラメーターは、関数呼び出し引数を非表示にします。そのため、私の例では、違いを強調するために、引数変数とパラメーター変数にそれぞれ名前を付けました。sgotestgotestssspq

あなたの例では、( &s0x4930d4変数のメモリ位置のアドレスであるs関数でmain関数呼び出しの引数として使用されgotest(s, done)、そして0x4974d8機能のためのメモリ位置のアドレスであるgotestパラメータss = nil関数の最後にパラメーターを設定gotestした場合smain;の変数には影響しません。sinmainsingotestは、異なるメモリ位置です。種類の面では、&sあり**Somethingsあり*Something、そして*sありますSomething&s(メモリ位置のアドレス)sへのポインタであり、タイプの匿名変数(メモリ位置のアドレス)へのポインタです。Something。値の面では、main.&s != gotest.&smain.s == gotest.smain.*s == gotest.*s、とmain.s.number == gotest.s.number

mkbの賢明なアドバイスを受けて、の使用をやめる必要がありprintln(&s)ます。fmtたとえば、パッケージを使用します。

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

同じメモリ位置を指す場合、ポインタの値は同じです。ポインタが異なるメモリ位置を指す場合、ポインタの値は異なります。


私の例では、gotestは「Something」へのポインターを受け取るので、同じオブジェクトを参照していると想定しています。これは、go-routine内の値を変更した後、オブジェクトの値もmain関数で変更されるためです。 。印刷されるポインタ値が異なります。
Jeroen Dirks 2011

@JamesDeanこの例では、ポインター値とタイプ** Somethingを出力していますが、これはポインター値のタイプ* Somethingとは異なります。ポインタを値で渡すように例を修正しました。
peterSO 2011

@James Deanポインタのアドレス(つまり、ポインタへのsポインタ)を出力しました。-ポインタは値で渡されます。のアドレスはsと同じではありませんs。gotest関数がprintln( s )組み込まれていない場合は、ポインター値が出力されます。
nos 2011

ああ今、私は何が起こっているのかわかります。println(&s)を実行することにより、ポインター値の代わりにポインターのアドレスを出力していました。println(s)を実行した場合、main関数とgoルーチン関数に同じポインターが表示されます。
Jeroen Dirks 2011

@JamesDeanその通り。GoではCのように、s * Somethingについては、&s、s、および* sの違いを知ることが不可欠です。
peterSO 2011

6

Goでは、引数は値で渡されます。

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

出力:

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)

4
組み込みのprintlnを使用するべきではありませんが、fmtパッケージから適切なものを使用してください:golang.org/doc/go_spec.html#Bootstrapping
mkb

2

どのようにしてください印刷囲碁オブジェクトのポインタ値を?

package main

import (
    "fmt"
)

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

結果:

0x1040a124

ポインタ値はどういう意味ですか?

ウィキペディアによると:

ポインタがメモリ内の場所を参照している


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

出力:

$ go run pointers.go
initial: 1
zeroval: 1
zeroptr: 0
pointer: 0x42131100
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.