我只是在玩Go,还没有一个很好的思维模型来说明 struct 何时按值传递或按引用传递.

这可能是一个非常愚蠢的问题,但我只想try 一下,看看我是仍然在处理同一个对象,还是已经复制了它(通过值传递).

有没有办法打印对象的指针(如果指针值被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 routine 中的指针值显示不同的值?原始对象上的数量确实发生了变化,因此它使用的是同一个对象.有没有办法查看持久的对象id?

推荐答案

Go函数参数按值传递.

首先,让我们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是存储器位置(&i)0xf800000040处具有初始值(i)42int变量.

在函数main中,p是指向存储器位置(&p)0xf8000000f0处的int变量的指针,其值(p=&i)0xf800000040指向int值(*p=i)42.

在函数main中,byval(p)是将存储器位置(&p)0xf8000000f0处的自变量的值(p=&i)0xf800000040分配给存储器位置(&q)0xf8000000d8处的函数byval参数q的函数调用.换句话说,内存被分配给byval参数q,并且main byval参数p的值被分配给它;pq的值最初是相同的,但是变量pq是不同的.

在函数byval中,使用作为指针p(*int)的副本的指针q(*int),将整数*q(i)设置为新的INT值4143.在回来之前.指针q被设置为nil(零值),这对p没有影响,因为q是副本.

在函数main中,p是指向存储器位置(&p)0xf8000000f0处的int变量的指针,其值为(p=&i)0xf800000040,该值指向新的int值(*p=i)4143.

在函数main中,i是存储器位置(&i)0xf800000040处的int变量,具有最终值(i)4143.

在您的示例中,用作函数gotest调用参数的函数main变量s与函数gotest参数s不同.它们具有相同的名称,但是具有不同作用域和内存位置的不同变量.函数参数s隐藏函数调用参数s.这就是为什么在我的例子中,我将参数和参数变量分别命名为pq,以强调差异.

在您的示例中,(&s)0x4930d4是函数main中用作函数调用gotest(s, done)的参数的变量s的内存位置的地址,而0x4974d8是函数gotest参数s的内存位置的地址.如果在函数gotest的末尾设置参数s = nil,则它对main中的变量s没有影响;main中的sgotest中的s是不同的内存位置.从类型上看,&s**Something,s*Something,*sSomething.&s是指向(存储器位置的地址)s的指针,其是指向类型Something的匿名变量(存储器位置的地址)的指针.就值而言,main.&s != gotest.&smain.s == gotest.smain.*s == gotest.*smain.s.number == gotest.s.number.

你应该接受mkb的明智建议,停止使用println(&s).例如,使用fmt软件包,

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

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

Go相关问答推荐

如何禁用Go SRC包中的VSCode警告?

语法-for循环中的initit陈述是否允许分配?

golang父进程的副本无法进行https/tls调用并获得tls:未能验证证书""

难以为多个平台添加Go Bazel构建选项

为什么工具链指令在这种情况下没有效果?

golang testscript .txtar 语法,用于 stderr 或 stdout 中包含的文本

如何模拟 stripe 需要 webhooks 的捕获事件?

Golang Docker Selenium Chrome

Exchange Web 服务 - 使用 soap xml 请求查找所有未读邮件

Golang crypto/rand 线程安全吗?

如何在 gocql 中设置最大池大小?

CBC Decrypter 解密加密文本,但部分文本被随机字符替换

致命错误:找不到由 zergon321/reisen 引起的libavcodec/avcodec.h文件

如何将一片十六进制字节转换为整数

未定义 protoc protoc-gen-go 时间戳

当有多个同名包时如何在vscode中显示golang包完整路径?

切片到数组指针的转换

如何 Select 前 N 个元素 Gin-Gorm

GoLang 遍历 yaml 文件

K8s 算子读取原始数据