Указатели
Указатели наглядно: что такое & и *, зачем они нужны и где Go прячет их от вас.
Указатель — это переменная, хранящая адрес другой переменной в памяти.
&xберёт адрес,*pчитает значение по адресу.
Зачем вообще указатели
По умолчанию Go передаёт аргументы в функции по значению — функция получает копию. Изменения копии не видны снаружи. Указатель позволяет передать адрес, чтобы функция меняла оригинал. Это первая причина. Вторая — избежать копирования больших структур.
& и *
Оператор & перед переменной даёт её адрес. Оператор * перед указателем — «разыменование», доступ к значению по адресу.
package main
import "fmt"
func main() {
x := 10
p := &x // p — указатель на x (тип *int)
fmt.Println(*p) // 10: значение по адресу
*p = 20 // меняем x через указатель
fmt.Println(x) // 20
}Вывод:
10 20
Передача в функцию: с указателем и без
Сравним две функции. Первая принимает int (копию) и не меняет оригинал. Вторая принимает *int (адрес) и меняет.
package main
import "fmt"
func incValue(n int) { n++ } // меняет копию
func incPointer(n *int) { *n++ } // меняет оригинал
func main() {
x := 5
incValue(x)
fmt.Println(x) // 5 — не изменилось
incPointer(&x)
fmt.Println(x) // 6 — изменилось
}Вывод:
5 6
nil и безопасность
Нулевое значение указателя — nil. Разыменование nil-указателя вызывает панику, поэтому перед *p иногда нужна проверка p != nil. Важно: в Go нет арифметики указателей (нельзя сдвигать указатель, как в C) — это сделано ради безопасности памяти.
Где Go прячет указатели
Хорошая новость: Go берёт многое на себя. К полю структуры через указатель обращаются так же, как напрямую — p.Field вместо громоздкого (*p).Field. А срезы, map и каналы и так ведут себя как ссылки, поэтому указатели на них почти не нужны.
package main
import "fmt"
type Point struct{ X, Y int }
func main() {
p := &Point{X: 1, Y: 2}
p.X = 99 // Go сам разыменует: эквивалент (*p).X
fmt.Println(*p)
}Вывод:
{99 2}Итог
- Go передаёт аргументы по значению; указатель даёт доступ к оригиналу.
&x— адрес,*p— значение по адресу; нулевой указатель —nil.- Арифметики указателей нет; к полям через указатель обращаются как
p.Field.