package main
import (
"fmt"
"sync"
)
type Data struct {
A []int32
B []string
}
func main() {
channel := make(chan Data)
data := Data{A: make([]int32, 0, 1), B: make([]string, 0, 1)}
fmt.Printf("Before gorountine addr, A: %p\n", data.A)
fmt.Printf("Before gorountine addr, B: %p\n", data.B)
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
for d := range channel {
fmt.Printf("goroutine addr before append, A: %p\n", d.A)
fmt.Printf("goroutine addr before append, B: %p\n", d.B)
d.A = append(d.A, 23)
d.B = append(d.B, "23")
//d.A[0] = 23
//d.B[0] = "23"
fmt.Printf("goroutine addr after append, A: %p\n", d.A)
fmt.Printf("goroutine addr after append, B: %p\n", d.B)
fmt.Println(d)
}
wg.Done()
}()
fmt.Printf("Before: %v\n", data)
channel <- data
close(channel)
wg.Wait()
fmt.Printf("After gorountine addr, A: %p\n", data.A)
fmt.Printf("After gorountine addr, B: %p\n", data.B)
fmt.Printf("After: %v\n", data)
}
代码见上
输出见下:
Before gorountine addr, A: 0xc20800a1dc
Before gorountine addr, B: 0xc20800a200
Before: {[] []}
goroutine addr before append, A: 0xc20800a1dc
goroutine addr before append, B: 0xc20800a200
goroutine addr after append, A: 0xc20800a1dc
goroutine addr after append, B: 0xc20800a200
{[23] [23]}
After gorountine addr, A: 0xc20800a1dc
After gorountine addr, B: 0xc20800a200
After: {[] []}
我的问题是:为什么slice的地址没有变,但是master goroutine里的data数据为空?
如果我去掉append,改为直接赋值,最后data则不为空
需要注意的是,我在make slice的时候特地设置cap为1
有疑问加站长微信联系(非本文作者)

各位大拿给出宝贵意见
看起来好难1
golang里的routine有没有自己的地址空间? 从第二段代码来看,应该是没有。
但是第一段代码怎么解释呢?
初学者,楼主如果是为了要修改data值,那么channel应该定义为*Data,运行后的结果就是会被修改。
至于为什么地址没有变的问题,楼主我们你是故意的么,%p,打印的是传参也是传值得啊,你每次都copy当然都是一样的啊。
请看下面的代码:
输出结果为:
s定义的是时候是nil,所以%p打印的时候的是nil,但是地址是有的,这个地址在用make初始化之后不变,但是值已经变了,不再是nil了,但是在append值之后,也是不会变的,但是地址变了啊。
最后一行,如果做一个copy,值仍然是相等的啊,所以不变,但是呢地址已经变了啊。
所以回到你的问题,你要定位问题,请把传递给printf的参数%p的变量加个&。
就是代码学习,所以不考虑别的方法绕过去。
我使用%p就是想打印地址,地址相同说明在append的时候没有重新分配空间(如非goroutine的进程有独立的地址空间)
另外,你仔细看我的代码。 我在给slice初始化的时候是设置cap为1的,所以不存在你说的地址变了的问题。 另外我答应地址的结果也证明了地址没有变化。
@buaatianwanli 可能是我没有说清楚,我的意思是你的打印了s的值,这个值是不变的,而实际上要打印的是s的地址,这个地址在复制的时候是变化的。你的代码使用channel传递的是值,在go routine对这个复制的值进行了修改,而在main routine中原来的值当然不受影响啦。
我明白你的意思。struct是拷贝的,但是struct里面是slice,是不是拷贝之后的那个struct中的slice指向的还是原来的struct中slice指向的内存地址呢?如果是,我打印任何一个struct都应该是相同的值,对吗?
同学,我还有一个意思是你的printf打印地址用错了!打印值v的地址要用&v,而不是v。所以你打印出来都是一样的,而且我用了一段代码来演示打印地址的时候用v和&v的区别。
地址好像是打错了。。。。你重新看看这个代码。
package main
结果:
我明白了,这个问题不是struct的值对应的slice是否变化的问题,本质是slice的底层数组表示。
你可能也注意到了,如果使用s = append(s, 1)和s[0]=1结果不太一样,对吧?
请看这段代码:
运行结果是:
也就是被copy变量和原变量是共用了底层数组,所以修改copy之后,原来的slice也会被修改(直到超出容量之后重新分配底层数组)。
对,我主要就是想讨论这个问题。 我在使用append的时候是这样初始化slice的:s := make([]int32, 0, 1) 这样,初始化一个空的slice,但是它的cap是1. 当我append的时候,虽然s的len(s)是1,但是append的时候却不会重新分配底层的数组。
另外一个问题就是,slice本身是由headSlice这个内部结构实现的。按理说说这样的操作: copy := s之后,©不应该和&s一样吧?
copy := s看错了
<iframe style="border:1px solid" src="https://wide.b3log.org/playground/b593d236d08d0463a4c1a7e1589f36aa.go?embed=true" width="99%" height="600"></iframe>
Before gorountine addr, A: 0xc20800a1dc(数组的首地址)-0xc20803a180(数组对象的地址) Before gorountine addr, B: 0xc20800a200(数组的首地址)-0xc20803a198(数组对象的地址) Before: {[] []} goroutine addr before append, A: 0xc20800a1dc(数组的首地址)-0xc20803a1e0(数组对象的地址),cap:1 goroutine addr before append, B: 0xc20800a200(数组的首地址)-0xc20803a1f8(数组对象的地址),cap:1 goroutine addr after append, A: 0xc20800a280(数组的首地址)-0xc20803a1e0(数组对象的地址),cap:2 goroutine addr after append, B: 0xc20801e100(数组的首地址)-0xc20803a1f8(数组对象的地址),cap:2 {[0 1] [0 1]} After gorountine addr, A: 0xc20800a1dc(数组的首地址)-0xc20803a180(数组对象的地址) After gorountine addr, B: 0xc20800a200(数组的首地址)-0xc20803a198(数组对象的地址) After: {[] []}
append两次地址当然变了。