Wie drucke ich den Zeigerwert eines Go-Objekts? Was bedeutet der Zeigerwert?


83

Ich spiele nur mit Go herum und habe noch kein gutes mentales Modell dafür, wann Strukturen nach Wert oder Referenz übergeben werden.

Dies mag eine sehr dumme Frage sein, aber ich möchte nur ein wenig experimentieren und sehen, ob ich noch an demselben Objekt arbeite oder eine Kopie davon erstellt habe (als Wert übergeben).

Gibt es eine Möglichkeit, den Zeiger (oder die interne ID, wenn der Zeigerwert durch gc geändert wird) eines Objekts zu drucken?

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

gibt auf meinem Windows (8g kompilierte Version):

0x4930d4
from gotest:
0x4974d8
42
0x4930d4
42

Warum zeigt der Zeigerwert innerhalb der go-Routine einen anderen Wert an? Die Menge auf dem ursprünglichen Objekt wurde geändert, sodass es mit demselben Objekt funktioniert. Gibt es eine Möglichkeit, eine Objekt-ID anzuzeigen, die dauerhaft ist?

Antworten:


114

Go-Funktionsargumente werden als Wert übergeben.

Lassen Sie uns zunächst die irrelevanten Teile Ihres Beispiels verwerfen, damit wir leicht erkennen können, dass Sie lediglich ein Argument nach Wert übergeben. Zum Beispiel,

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

Ausgabe:

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

In Funktion main, iist eine intVariable an der Speicherstelle ( &i) 0xf800000040mit einem Anfangswert ( i) 42.

In Funktion main, pist ein Zeiger auf eine intVariable an der Speicherstelle ( &p) 0xf8000000f0mit einem Wert ( p= &i) , 0xf800000040welche Punkte auf einen intWert ( *p= i) 42.

In Funktion main, byval(p)ist ein Funktionsaufruf, der den Wert (teilt p= &i) 0xf800000040des Arguments an der Speicherstelle ( &p) 0xf8000000f0an den byvalFunktionsparameter qan der Speicherstelle ( &q) 0xf8000000d8. Mit anderen Worten, dem byvalParameter wird Speicher zugewiesen , qund der Wert des main byvalArguments pwird ihm zugewiesen. Die Werte von pund qsind anfangs gleich, aber die Variablen pund qsind unterschiedlich.

In der Funktion byvalwird integer ( ) unter Verwendung von pointer q( *int), einer Kopie von pointer p( *int), auf einen neuen int-Wert gesetzt . Am Ende vor der Rückkehr. Der Zeiger wird auf (Nullwert) gesetzt, was keine Auswirkung hat, da es sich um eine Kopie handelt.*qi4143qnilpq

In Funktion main, pist ein Zeiger auf eine intVariable an der Speicherstelle ( &p) 0xf8000000f0mit einem Wert ( p= &i) , 0xf800000040welche Punkte auf einen neuen intWert ( *p= i) 4143.

In Funktion main, iist eine intVariable an der Speicherstelle ( &i) 0xf800000040mit einem Endwert ( i) 4143.

In Ihrem Beispiel die Funktion mainVariable sals Argument an die Funktion gotestAufruf ist nicht das gleiche wie die Funktion gotestParameter s. Sie haben denselben Namen, sind jedoch unterschiedliche Variablen mit unterschiedlichen Bereichen und Speicherorten. Der Funktionsparameter sverbirgt das Funktionsaufrufargument s. Deshalb in meinem Beispiel habe ich die Argumentation und Parametervariablen genannt pund qjeweils die Differenz zu betonen.

In Ihrem Beispiel ( &s) 0x4930d4ist die Adresse des Speicherplatzes für die Variable sin der Funktion , maindie als Argument für den Funktionsaufruf verwendet wird gotest(s, done), und 0x4974d8ist die Adresse des Speicherplatzes für den gotestFunktionsparameter s. Wenn Sie den Parameter s = nilam Ende der Funktion festlegen gotest, hat dies keine Auswirkungen auf die Variable sin main. sin mainund sin gotestsind unterschiedliche Speicherorte. In Bezug auf Typen &sist **Something, sist *Somethingund *sist Something. &sist ein Zeiger auf (Adresse des Speicherorts) s, der ein Zeiger auf (Adresse des Speicherorts) eine anonyme Variable vom Typ istSomething. In Bezug auf die Werte, main.&s != gotest.&s, main.s == gotest.s, main.*s == gotest.*s, und main.s.number == gotest.s.number.

Sie sollten den weisen Rat von mkb befolgen und die Verwendung einstellen println(&s). Verwenden Sie das fmtPaket zum Beispiel,

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

Zeiger haben denselben Wert, wenn sie auf denselben Speicherort zeigen. Zeiger haben unterschiedliche Werte, wenn sie auf unterschiedliche Speicherorte zeigen.


In meinem Beispiel nimmt der gotest einen Zeiger auf 'Something', also gehe ich davon aus, dass er sich auf dasselbe Objekt bezieht, und es ist klar, dass nach dem Ändern des Werts innerhalb der go-Routine auch der Wert des Objekts in der Hauptfunktion geändert wurde . Der gedruckte Zeigerwert ist unterschiedlich.
Jeroen Dirks

@JamesDean In Ihrem Beispiel drucken Sie den Zeigerwert & s Typ ** Etwas, das nicht mit dem Typ des Zeigerwerts * Etwas identisch ist. Ich habe mein Beispiel überarbeitet, um einen Zeiger nach Wert zu übergeben.
PeterSO

@James Dean Sie haben die Adresse des Zeigers ausgedruckt (dh einen Zeiger auf den sZeiger). - Zeiger werden als Wert übergeben, die Adresse von sist nicht dieselbe wie s. Wenn Ihre gotest-Funktion println( s )intead wäre, würde sie den Zeigerwert ausgeben.
Nr.

Oh, jetzt sehe ich, was los ist. Mit println (& s) habe ich die Adresse des Zeigers anstelle des Zeigerwerts gedruckt. Wenn ich println (s) gemacht hätte, hätte es den gleichen Zeiger in der Haupt- und der Go-Routinefunktion gezeigt.
Jeroen Dirks

@ JamesDean Genau. In Go wie in C ist es für s * Something wichtig, den Unterschied zwischen & s, s und * s zu kennen.
PeterSO

6

In Go werden Argumente als Wert übergeben.

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

Ausgabe:

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
Sie sollten den eingebauten Druck nicht verwenden, sondern etwas Passendes aus dem fmt-Paket verwenden: golang.org/doc/go_spec.html#Bootstrapping
mkb

2

Wie kann ich drucken den Zeigerwert eines Go - Objekt?

package main

import (
    "fmt"
)

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

Ergebnisse in:

0x1040a124

Was bedeutet der Zeigerwert?

Laut Wikipedia :

Ein Zeiger verweist auf eine Stelle im Speicher


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

AUSGABE:

$ go run pointers.go
initial: 1
zeroval: 1
zeroptr: 0
pointer: 0x42131100
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.