【问题标题】:Convert slice of string to slice of pointer to string将字符串切片转换为指向字符串的指针切片
【发布时间】:2018-01-26 10:47:25
【问题描述】:

我想将字符串切片转换成指向字符串的指针切片

values1 := []string{"a", "b", "c"}
var values2 []*string
for _, v := range values1 {
    fmt.Printf("%p | %T\n", v, v)
    values2 = append(values2, &v)
}
fmt.Println(values2)

%!p(string=a) => 字符串
%!p(string=b) => 字符串
%!p(string=c) => 字符串
[0xc42000e1d0 0xc42000e1d0 0xc42000e1d0]

据我了解,
我的变量v 似乎是一个字符串,而不是指向字符串的指针。
因此v在迭代时应该从values1复制过来。

显然我不正确,因为v 仍然有相同的地址0xc42000e1d0
v 不是指针,值怎么变化?

如需快速解决方案,请使用:

values1 := []string{"a", "b", "c"}
var values2 []*string
for i, _ := range values1 {
    values2 = append(values2, &values1[i])
}

【问题讨论】:

  • 有什么问题?第一个版本不起作用,因为只有一个循环变量(在每次迭代中重用),并且您在每次迭代中附加相同的地址。查看示例 onetwo
  • @icza 更新了问题

标签: string pointers for-loop go memory


【解决方案1】:

第一个版本不起作用,因为只有一个循环变量v,它每次迭代都有相同的地址。循环变量在每次迭代中被赋值,它改变它的值而不是它的地址,并且你在每次迭代中附加这个相同的地址。

您提出的一种可能的解决方案是:附加原始切片的正确切片元素的地址:

for i := range values1 {
    values2 = append(values2, &values1[i])
}

这可行,但请注意values2 包含指向values1 的后备数组的地址,因此values1 的后备数组将保留在内存中(不会被垃圾收集),直到values1values2 变量无法访问。另请注意,修改values1 的元素会间接影响values2,因为values2 的元素指向values1 的元素。

另一种解决方案是创建临时局部变量并附加它们的地址:

for _, v := range values1 {
    v2 := v
    values2 = append(values2, &v2)
}

通过这样做,您的values2 切片将不会阻止values1 被垃圾收集,同时修改values1 中的值也不会反映在values2 中,它们将是独立的。

查看有关循环变量的相关问题:

Why do these two for loop variations give me different behavior?

Golang: Register multiple routes using range for loop slices/map

【讨论】:

  • 感谢有关在临时变量中复制值的提示
【解决方案2】:

for .. range 子句中的迭代值是为每次迭代分配的。

spec 明确指出:

迭代值分配给各自的迭代 赋值语句中的变量。

这相当于做:

var v string
v = string1
values[0] = &v
v = string2
values[1] = &v
etc...

变量v 仍然存在于内存的同一区域,只是内容发生了变化。所以v的地址永远不会变。

【讨论】:

    猜你喜欢
    • 2018-09-15
    • 2017-01-10
    • 2021-03-02
    • 2019-01-24
    • 2017-09-21
    • 2020-11-27
    • 1970-01-01
    • 2015-05-01
    • 2015-05-15
    相关资源
    最近更新 更多