如何打印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)
}

在我的Windows(8g编译版本)上给出:

0x4930d4
from gotest:
0x4974d8
42
0x4930d4
42

为什么go例程中的指针值显示不同的值?原始对象上的数量确实发生了更改,因此它正在使用同一对象。有没有办法查看持久的对象ID?

Answers:


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

在函数中mainiint位于内存位置(&i)的0xf800000040具有初始值(i)的变量42

在函数中mainp是指向int内存位置(&p)的变量的指针,该变量0xf8000000f0的值(p= &i0xf800000040指向int值(*p= i42

在功能mainbyval(p)是一个函数调用,其将值(p= &i0xf800000040在存储器位置(自变量的&p0xf8000000f0给函数byval参数q在存储器位置(&q0xf8000000d8。换句话说,存储器被分配用于byval参数q和的值main byval的参数p被分配给它; 的值pq最初是相同的,但变量pq是不同的。

在功能byval,使用指针q*int),它是指针的一个拷贝p*int),整数*qi)被设置为一个新的int值4143。最后返回之前。指针q设置为nil(零值),p由于q是副本,因此不起作用。

在函数中mainp是指向int内存位置(&p)的变量的指针,该变量0xf8000000f0的值(p= &i0xf800000040指向新int值(*p= i4143

在函数中mainiint位于内存位置(&i)的变量,0xf800000040具有最终值(i4143

在您的示例中,用作函数调用参数的函数main变量与function参数不同。它们具有相同的名称,但是是具有不同作用域和存储位置的不同变量。函数参数隐藏函数调用参数。这就是为什么在我的示例中,我分别命名参数和参数变量并强调差异。sgotestgotestssspq

在您的示例中,(&s0x4930d4s函数main中变量的存储位置的地址,该变量用作函数调用的参数gotest(s, done),并且0x4974d8是函数gotest参数的存储位置的地址s。如果s = nil在函数末尾设置参数gotest,则它对sin中的变量没有影响mainsinmainsingotest是不同的内存位置。就类型而言,&sis **Somethingsis*Something*sis 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)

当指针指向相同的存储位置时,它们具有相同的值。指针指向不同的内存位置时,它们具有不同的值。


在我的示例中,getest指向“ Something”的指针,因此我假设它引用的是同一对象,这显然是因为在更改例程中的值之后,该对象在主函数中的值也已更改。所打印的指针值不同。
Jeroen Dirks

@JamesDean在您的示例中,您正在打印指针值的类型** Something,这与指针值的类型* Something不同。我修改了示例以按值传递指针。
peterSO 2011年

@James Dean您已打印出指针的地址(即指向该指针的s指针),-指针按值传递,的地址s与相同s。如果您的getest函数确实起作用println( s ),它将打印指针值。

哦,现在我明白了。通过执行println(&s),我正在打印指针的地址而不是指针的值。如果我完成了println(s),它将在main和go例程函数中显示相同的指针。
Jeroen Dirks

@JamesDean确实如此。在C语言中的Go中,对于s *,必须了解&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

如何打印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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.