Структуры и указатели: объявление, инициализация и передача по адресу
Содержание курса
Передача struct по значению и по указателю: видимость изменений в вызывающем коде
Возьмём конкретный пример. Есть структура и две функции:
type Point struct {
X int
Y int
}
func scale(p Point, f int) {
p.X *= f
p.Y *= f
}
func scalePtr(p *Point, f int) {
p.X *= f
p.Y *= f
}
Что произойдёт в каждом случае:
func main() {
a := Point{X: 3, Y: 4}
scale(a, 10)
fmt.Println(a) // {3 4} — ничего не изменилось
scalePtr(&a, 10)
fmt.Println(a) // {30 40} — изменилось
}
Почему так? При вызове scale(a, 10) Go копирует всё содержимое a в новую переменную p внутри функции. p.X и p.Y — это отдельные поля в отдельной области памяти. Функция умножает их, но оригинальный a об этом ничего не знает.
При вызове scalePtr(&a, 10) функция получает адрес a. Когда внутри функции выполняется p.X *= f, это разворачивается в изменение (*p).X — то есть поля того самого a в вызывающем коде.
Это фундаментальное отличие от срезов, с которыми вы работали раньше. Срез — это заголовок с указателем на массив; копирование заголовка оставляет общий массив. Структура с числовыми полями копируется полностью: после копирования нет никакой связи между оригиналом и копией.
Практическое правило для структур со скалярными полями: если функция должна изменить данные и вы хотите видеть изменения снаружи — передавайте *T. Если функция только читает или должна работать с независимой копией — передавайте T.
