Почему строковый указатель в поведении golang не интуитивен в цикле диапазона? - PullRequest
0 голосов
/ 09 июля 2019

С этим кодом: https://play.golang.org/p/tCm1W-K-6ob

Этот код будет печатать: [c c c], но [a b c] исключается.

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()
}

Почему список не ["a", "b", "c"]? Что случилось с диапазоном или &string?

Ответы [ 4 ]

1 голос
/ 09 июля 2019

Все элементы содержат адрес одной локальной переменной v.

Если ваша цель состоит в том, чтобы назначить адрес элемента слайса в поле a, выполните следующие действия:

for i := range slist {
    item := &A{
        a: &slist[i],  // address of element instead of local variable.
    }
    list[i] = item
}

Запустите его на игровой площадке Go .

Вы также можете получить желаемый результат, создав новую переменную на каждой итерации цикла:

for i, v := range slist {
    v := v  // create new value on each iteration.
    item := &A{
        a: &v,
    }
    list[i] = item
}

Запустите его на игровой площадке Go .

1 голос
/ 09 июля 2019

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

значение f в range имеет тот же адрес

0 голосов
/ 09 июля 2019

Дело в том, что переменная итератора назначается только один раз, и значение, на которое она указывает, изменяется при каждой итерации цикла:

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()
}

Запуск на площадке

Однако у меня есть сильное чувство, что это не интуитивно понятно, потому что мы в значительной степени говорим о преждевременной оптимизации.(Немного упрощенно): Строка в Go - это, в основном, фрагмент байтов ([]byte), причем фрагмент уже является указателем на массив поддержки.Подробнее см. Go Slices: использование и внутреннее оборудование .

0 голосов
/ 09 июля 2019

Вы можете попробовать это

Здесь я могу запустить ваш код и внести некоторые изменения.

https://play.golang.org/p/GmU3goeOKdF

вот код

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()
}

Выход abc

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...