Возвращает ли разыменование структуры новую копию структуры?

Почему, когда мы ссылаемся на struct, используя (*structObj), Go, похоже, возвращает новую копию structObj, а не возвращает тот же адрес оригинала structObj? Это может быть моим недоразумением, поэтому я прошу разъяснений

package main

import (
    "fmt"
)

type me struct {
    color string
    total int
}

func study() *me {
    p := me{}
    p.color = "tomato"
    fmt.Printf("%p\n", &p.color)
    return &p
}

func main() {
    p := study()
    fmt.Printf("&p.color = %p\n", &p.color)

    obj := *p
    fmt.Printf("&obj.color = %p\n", &obj.color)
    fmt.Printf("obj = %+v\n", obj)

    p.color = "purple"
    fmt.Printf("p.color = %p\n", &p.color)
    fmt.Printf("p = %+v\n", p)
    fmt.Printf("obj  = %+v\n", obj)

    obj2 := *p
    fmt.Printf("obj2 = %+v\n", obj2)
}

Выход

0x10434120
&p.color = 0x10434120
&obj.color = 0x10434140   //different than &p.color!
obj = {color:tomato total:0}
p.color = 0x10434120
p = &{color:purple total:0}
obj  = {color:tomato total:0}
obj2 = {color:purple total:0} // we get purple now when dereference again

Иди на площадку

Ответ 1

Когда вы пишете

obj := *p

Вы копируете значение структуры, на которое указывает p (* разыменовывает p). Это похоже на:

var obj me = *p

Итак, obj - это новая переменная типа me, которая инициализируется значением *p. Это приводит к тому, что obj имеет другой адрес памяти.

Обратите внимание, что obj имеет тип me, а p имеет тип *me. Но это отдельные ценности. Изменение значения поля obj не повлияет на значение этого поля в p (если только структура me не имеет ссылочного типа в качестве поля, то есть среза, карты или каналов. См. здесь и здесь.). Если вы хотите добиться такого эффекта, используйте:

obj := p
// equivalent to: var obj *me = p

Теперь obj указывает на тот же объект, что и p. Они по-прежнему имеют разные адреса, но содержат в себе один и тот же адрес фактического объекта me.

Ответ 2

Нет, "назначение" всегда создает копию в Go, включая назначение аргументов функции и метода. Оператор obj := *p копирует значение *p в obj.

Если вы измените оператор p.color = "purple" на (*p).color = "purple", вы получите тот же результат, потому что разыменование p сам не создает копию.

Ответ 3

tl;dr Разыменование (с помощью оператора *) в Go не делает копию. Возвращает значение, на которое указывает указатель.