新人求教:一个关于Go指针的问题

ticsmtc · · 992 次点击
我觉得你可能正好触发了一个golang的优化陷阱 考察以下代码 ```go package main import "fmt" func main() { slist := SimpleTest() fmt.Printf("%+#v\n", slist) } type S struct { Data int Children []S } func SimpleTest() (tree []S) { tree = make([]S, 0) points := make([]*S, 0) for index := 0; index <= 5; index++ { tree = append(tree, S{ Data: index, Children: make([]S, 0), }) s := tree[index] //标记1 points = append(points, &s) } //标记2 //for index := range tree { // points = append(points, &(tree[index])) //} for index := range tree { // 区别在这里 tree[index].Children = append(tree[index].Children,S{ Data: tree[index].Data * 10, Children: make([]S, 0), }) } return } ``` 输出结果 ```shell []main.S{main.S{Data:0, Children:[]main.S{main.S{Data:0, Children:[]main.S{}}}}, main.S{Data:1, Children:[]main.S{main.S{Data:10, Children:[]main.S{}}}}, main.S{Data:2, Children:[]main.S{main.S{Data:20, Children:[]main.S{}}}}, main.S{Data:3, Children:[]main.S{main.S{Data:30, Children:[]main.S{}}}}, main.S{Data:4, Children:[]main.S{main.S{Data:40, Children:[]main.S{}}}}, main.S{Data:5, Children:[]main.S{main.S{Data:50, Children:[]main.S{}}}}} ``` 你的标记1无效 标记2有效 是因为你在标记2中再次使用了返回值tree, 阻止了golang的编译器过早把tree优化到返回栈里. 标记1使用过tree之后后面没有再次使用, golang会认为tree的值不会改变了 所以会出现你发现的这个奇怪的问题. 而且你这个程序本身思路也比较奇葩, point压根就没啥用处...
#1
更多评论
非常感谢解答! 1. 按照优化的思路来说,似乎标记1和标记2还是应该等价的? 至少在标记2 之后也没有再次用到tree,当然了,优化这种玄学也不排除2可以但是1不行的情况,确实是个思路 2.这个问题来源于我司的业务代码,提问不能放实际代码(太长了主要)这段代码主要意在提炼问题本质而不是讨论代码合理性,所以修改后的代码没有参考意义,这里实际上还是讨论 “为什么指针地址一样但是修改不了” 的问题。 3.在问题的`30`行的 `for`之前,我检查过两种方式,`points` 和 `tree` 的地址是一一对应的,也就是说后续的 `points` 操作的地址仍然是 `tree` 的地址,莫非优化之后,赋值就失败了吗? 最后还是再次感谢提供了新的思路
#2
其实我上面已经回答了, tree已经提前进入了返回栈, 所以1和2并不是等价的. 标记1后面对points的任何修改和赋值都不会影响到tree了, 进一步说虽然points的指针是指向 tree的children, 但实际上你的children持有的不是指针只是一个复制的副本而已. 这个被修改的副本并没有被 tree携带返回.
#3