1. ホーム
  2. go

[解決済み] Go オブジェクトのポインタ値を表示するにはどうすればよいですか?ポインタの値は何を意味するのですか?

2023-04-04 06:24:49

質問

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

は、私のWindows(8gコンパイル版)上で動作します。

0x4930d4
from gotest:
0x4974d8
42
0x4930d4
42

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

どのように解決するのですか?

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

機能では main , iint 変数で、メモリ位置 ( &i ) 0xf800000040 を初期値として ( i ) 42 .

関数内 main , p へのポインタです。 int 変数へのポインタです。 &p ) 0xf8000000f0 という値を持つ ( p = &i ) 0xf800000040 を指している int の値 ( *p = i ) 42 .

関数内 main , byval(p) は関数呼び出しで、値 ( p = &i ) 0xf800000040 メモリ位置の引数の( &p ) 0xf8000000f0 を関数 byval パラメータ q をメモリ位置( &q ) 0xf8000000d8 . つまり、メモリが確保されるのは byval パラメータ q の値と main byval 引数 p が代入されます。 pq は最初は同じですが、変数 pq は区別されます。

関数では byval で、ポインタを用いて q ( *int ) のコピーであり、これはポインタ p ( *int ), 整数 *q ( i ) には新しい int 値が設定されます。 4143 . 最後にリターンする前に、ポインタ q に設定されます。 nil (ゼロ値) であり、これは p であるため q はコピーだからです。

関数では main , p へのポインタです。 int 変数へのポインタです。 &p ) 0xf8000000f0 という値を持つ ( p = &i ) 0xf800000040 を指し、新しい int の値 ( *p = i ) 4143 .

関数内 main , iint 変数で、メモリ位置 ( &i ) 0xf800000040 を最終値として ( i ) 4143 .

あなたの例では、関数 main 変数 s 関数の引数として使われる gotest の呼び出しは、関数 gotest パラメータ s . これらは同じ名前ですが、スコープとメモリ位置が異なる別の変数です。関数パラメータ s は関数呼び出しの引数 s . このため、私の例では、引数とパラメータの変数名を pq をそれぞれ使って違いを強調しています。

あなたの例では、( &s ) 0x4930d4 は変数のメモリ位置のアドレスです。 s 関数内の main は、関数呼び出しの引数として使用されます。 gotest(s, done)0x4974d8 は関数のメモリ位置のアドレスです。 gotest パラメータ s . もし、パラメータ s = nil を関数の最後に設定すると gotest に対して何の効果もありません。 smain ; smainsgotest は異なるメモリ位置である。タイプで言えば &s**Something , s*Something であり、かつ *sSomething . &s は (メモリ位置のアドレス) へのポインタです。 s 型の無名変数へのポインタである。 Something . 値の観点からは main.&s != gotest.&s , main.s == gotest.s , main.*s == gotest.*s そして main.s.number == gotest.s.number .

mkb の賢明なアドバイスに従って、あなたは println(&s) . を使いましょう。 fmt というパッケージを使用します。

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

ポインターは、同じメモリ位置を指しているときは同じ値を持ち、異なるメモリ位置を指しているときは異なる値を持ちます。