Как разыменование указателя работает в Go?
Я изучаю учебники golang в http://tour.golang.org/ и немного экспериментировал с некоторыми вещами в пример 29
Для справки здесь копируется исходный пример:
package main
import "fmt"
type Vertex struct {
X, Y int
}
var (
p = Vertex{1, 2} // has type Vertex
q = &Vertex{1, 2} // has type *Vertex
r = Vertex{X: 1} // Y:0 is implicit
s = Vertex{} // X:0 and Y:0
)
func main() {
fmt.Println(p, q, r, s)
}
Это довольно простой пример, показывающий, как создавать экземпляры этой фантастической новой структуры, Vertex
. Пример 28, однако, показывает манипуляцию вершиной с помощью указателя на нее, поэтому я немного изменил пример и был удивлен выходом. Вот модификация:
func main() {
t := *q
q.X = 4
u := *q
fmt.Println(p, q, r, s, t, u, t == u)
}
И вывод:
{1 2} &{4 2} {1 0} {0 0} {1 2} {4 2} false
Меня удивило то, что t
не {4, 2}, что, по-видимому, означает, что изменение q.X
изменило экземпляр структуры, на которую указывает q
. Исходя из фона C/С++, это кажется мне очень странным поведением.
Итак, что на самом деле происходит здесь? Почему использование q.X = 4
для изменения Вершины не распространяется на t
?
Ответы
Ответ 1
t := *q
создает копию структуры, на которую указывает q
.
Если вы хотите наблюдать изменения в q
через t
, тогда придерживайтесь указателя:
func main() {
t := q
q.X = 4
u := *q
fmt.Println(p, q, r, s, t, u, *t == u)
}
Это приведет к тому, что вы, вероятно, искали.
{1 2} &{4 2} {1 0} {0 0} &{4 2} {4 2} true
Я не уверен, что тебе кажется чрезвычайно странным. C и С++ ведут себя одинаково. Рассмотрим следующее:
#include <iostream>
struct Vertex
{
int x;
int y;
};
std::ostream& operator<<(std::ostream& out, const Vertex& v)
{
out << "{ " << v.x << ", " << v.y << " }";
return out;
}
int main()
{
Vertex v = Vertex{1, 2};
Vertex* q = &v;
Vertex t = *q;
q->x = 4;
std::cout << "*q: " << *q << "\n";
std::cout << " t: " << t << "\n";
}
Результат этого кода на С++ показывает то же поведение:
*q: { 4, 2 }
t: { 1, 2 }