问题
With this code: https://play.golang.org/p/tCm1W-K-6ob
This code would print: [c c c]
, but [a b c]
is excepted.
type A struct {
a *string
}
func f() {
slist := []string{"a", "b", "c"}
list := make([]*A, len(slist))
for i, v := range slist {
item := &A{
a: &v,
}
list[i] = item
}
fmt.Printf("[%s %s %s]", *list[0].a, *list[1].a, *list[2].a)
}
func main() {
f()
}
Why the list is not ["a", "b", "c"]
? What happened to the range or the &string
?
回答1:
https://play.golang.org/p/utJrUmxeqfh
package main
import (
"fmt"
)
func main() {
foo := []int{1, 2, 3}
for _, f := range foo {
fmt.Printf("value is %d, addr is %p \n", f, &f)
}
fmt.Println("Hello, playground")
}
value is 1, addr is 0x414020
value is 2, addr is 0x414020
value is 3, addr is 0x414020
Hello, playground
the value f
in range
has the same address
回答2:
The items all contain the address of the one local variable v
.
If your goal is to assign the address of the slice element the a
field, then do the following:
for i := range slist {
item := &A{
a: &slist[i], // address of element instead of local variable.
}
list[i] = item
}
Run it on the Go Playground.
You can also get the output you want by creating a new variable on each loop iteration:
for i, v := range slist {
v := v // create new value on each iteration.
item := &A{
a: &v,
}
list[i] = item
}
Run it on the Go Playground.
回答3:
The thing is that the iterator variable is allocated only once and the value it points to is changed with each iteration of the loop:
package main
import (
"fmt"
)
type A struct {
a *string
}
func f() {
slist := []string{"a", "b", "c"}
list := make([]*A, len(slist))
// v is only allocated once and the value at it's address is changed
// on each iteration.
for i, v := range slist {
fmt.Printf("Iteration %d: address of v %#v, value of v \"%s\"\n", i+1, &v, v)
// Add in a temp variable, which will get allocated
// and hence have a new address each iteration.
t := v
// Now assign that temp variable's address to the Item's a.
list[i] = &A{
a: &t,
}
}
fmt.Printf("[%s %s %s]", *list[0].a, *list[1].a, *list[2].a)
}
func main() {
f()
}
Run on playground
However, I have a strong feeling that this is unintuitive because we are pretty much talking of premature optimization. (A bit simplified): A string in Go is basically a slice of bytes([]byte
), with a slice already being a pointer to the backing array. See Go Slices: usage and internals for details.
回答4:
You can try this
Here I can run your code and do some changes.
https://play.golang.org/p/GmU3goeOKdF
here is code
package main
import (
"fmt"
)
type A struct {
a *string
}
func f() {
slist := []string{"a", "b", "c"}
list := make([]*A, len(slist))
for i, v := range slist {
item := &A{
a: &v,
}
list[i] = item
fmt.Printf("%s", *list[i].a)
}
}
func main() {
f()
}
Output abc
来源:https://stackoverflow.com/questions/56944781/why-is-string-pointer-in-golang-behavior-counter-intuitive-in-range-loop