Как распечатать значение указателя объекта Go? Что означает значение указателя?


83

Я просто играю с Go и еще не имею хорошей мысленной модели того, когда структуры передаются по значению или по ссылке.

Это может быть очень глупый вопрос, но я просто хочу немного поэкспериментировать и посмотреть, работаю ли я над тем же объектом или я сделал его копию (передал по значению).

Есть ли способ распечатать указатель (или внутренний идентификатор, если значение указателя изменяется с помощью gc) объекта?

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 показывает другое значение? Количество на исходном объекте действительно изменилось, поэтому он работал с тем же объектом. Есть ли способ увидеть постоянный идентификатор объекта?

Ответы:


116

Аргументы функции 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, iявляется intпеременным в ячейке памяти ( &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; значения pи qизначально одинаковы, но переменные pи qразличны.

В функции byvalпри использовании pointer q( *int), который является копией pointer p( *int), integer *q( i) устанавливается на новое значение типа int 4143. В конце перед возвращением. указатель qустановлен в nil(нулевое значение), что не имеет никакого эффекта, pпоскольку qявляется копией.

В функции main, pявляется указателем на intпеременную в ячейке памяти ( &p) 0xf8000000f0со значением ( p= &i) , 0xf800000040которая указывает на новое intзначение ( *p= i) 4143.

В функции main, iявляется intпеременным в ячейке памяти ( &i) 0xf800000040с конечным значением ( i) 4143.

В вашем примере mainпеременная функции, sиспользуемая в качестве аргумента для gotestвызова функции, не совпадает с gotestпараметром функции s. У них одно и то же имя, но это разные переменные с разным объемом и расположением в памяти. Параметр функции sскрывает аргумент вызова функции s. Вот почему в моем примере я назвал переменные аргумента и параметра pи, qсоответственно, чтобы подчеркнуть разницу.

В вашем примере ( &s) 0x4930d4- это адрес ячейки памяти для переменной sв функции, mainкоторая используется в качестве аргумента для вызова функции gotest(s, done), и 0x4974d8адрес ячейки памяти для gotestпараметра функции s. Если вы установите параметр s = nilв конце функции gotest, он не повлияет на переменную sв main; sin mainи sin gotest- разные места в памяти. С точки зрения типов, &sесть **Something, sесть *Somethingи *sесть Something. &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)

Указатели имеют одинаковое значение, когда они указывают на одну и ту же ячейку памяти; указатели имеют разные значения, когда они указывают на разные участки памяти.


В моем примере gotest принимает указатель на 'Something', поэтому я предполагаю, что он ссылается на тот же объект, и это очевидно, поскольку после того, как я изменил значение внутри go-подпрограммы, объект также изменил свое значение в основной функции . Напечатанное значение указателя отличается.
Jeroen Dirks

@JamesDean В вашем примере вы печатаете значение указателя & s type ** Something, что не то же самое, что значение указателя s type * Something. Я изменил свой пример, чтобы передать указатель по значению.
peterSO

@James Dean Вы распечатали адрес указателя (т.е. указатель на sуказатель), - указатели передаются по значению, адрес sне совпадает с s. Если ваша функция gotest действительно println( s )заинтересовалась, она напечатает значение указателя.

О, теперь я понимаю, что происходит. Выполняя println (& s), я печатал адрес указателя вместо значения указателя. Если бы я сделал println (s), он бы показал один и тот же указатель в функции main и функции go.
Jeroen Dirks

@JamesDean Совершенно верно. В Go, как и в C, для s * Something важно знать разницу между & s, s и * s.
peterSO

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

Как распечатать значение указателя объекта Go?

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.