我认为这个问题曾问过几次,但我仍然感到困惑:
我有以下代码:
type obj struct {
s *string
}
var cmdsP = []*string {
stringPointer("create"),
stringPointer("delete"),
stringPointer("update"),
}
var cmds = []string {
"create",
"delete",
"update",
}
// []*string
func loop1() {
slice := make([]obj, 0, 0)
for _, cmd := range cmdsP {
slice = append(slice, obj{cmd})
}
for _, o := range slice {
fmt.Println(*o.s)
}
}
// []string
func loop2() {
slice := make([]obj, 0, 0)
for _, cmd := range cmds {
slice = append(slice, obj{&cmd})
}
for _, o := range slice {
fmt.Println(*o.s)
}
}
func stringPointer(v string) *string {
return &v
}
https://play.golang.org/p/65Le_8Pi3Mi
唯一的区别在于切片语义[]*string
和[]string
如何改变cmd
变量的行为?您能否详细地画出或解释一下,通过两个循环在迭代过程中内存中会发生什么?
答案 0 :(得分:4)
当您在集合上调用range
时,go运行时将初始化2个内存位置。一个用于索引(在本例中为_
),另一个用于值cmd
。
那么范围是将集合中的每个项目都提取并复制到您调用range
时创建的存储位置。
这意味着切片中的每个项目都被一个一个地放入该内存位置。
当您执行&cmd
时,您正在使用指针。该指针指向将每个切片项目复制到的共享内存位置。
所有使用&cmd
创建的指针都指向相同的内存位置。
这意味着完成range
之后,指针指向的那个存储位置中剩下的唯一值就是range
迭代中的最后一个值。
这就是为什么您得到输出的原因
update
update
update
答案 1 :(得分:1)
这是因为在一种情况下,您将地址传递给struct,而在另一种情况下,您将传递字符串值。因此,每当将struct字段追加到切片时,它将更新同一地址上的现有值。这就是为什么您只获得指针类型的结构切片的最后一个值的原因,在这种情况下为update
// []string
func loop2() {
slice := make([]obj, 0, 0)
for _, cmd := range cmds {
slice = append(slice, obj{&cmd})
}
for _, o := range slice {
fmt.Println(*o.s)
}
}
&cmd
将指向相同的地址,该地址将在每次迭代中将其值更新到相同的位置,而不是在cmd
中不将地址传递给loop1
的情况。 / p>
已编辑
这是[]obj
的一部分,其指针类型为* string
slice := make([]obj, 0, 0)
因此,当您在其上循环时,实际上是在传递cmd
的地址并将其分配给指针字段。
要查看差异,请打印类型,您将获得例如以下信息:
func loop1() {
slice := make([]string, 0, 0)
for _, cmd := range cmdsP {
slice = append(slice, *cmd)
}
for _, o := range slice {
fmt.Println(o)
}
fmt.Printf("%#v\n", slice)
}
在上述情况下,您正在创建一个字符串切片。
// []*string
func loop1() {
slice := make([]obj, 0, 0)
for _, cmd := range cmdsP {
slice = append(slice, obj{cmd})
}
for _, o := range slice {
fmt.Println(*o.s)
}
fmt.Printf("%#v\n", slice)
}
在第二种情况下,您将创建一个obj
结构切片,其中包含指向string
的指针字段。他们都是不同的情况。使用fmt
包查看您在loop1
函数中创建的切片的实际类型