Указатель — это переменная, которая хранит адрес памяти другой переменной, а не её значение. В Go указатели имеют тип с префиксом *
, например *int
— указатель на целое число.
var x int = 10
var p *int = &x // p содержит адрес x
&
):x := 42
ptr := &x // ptr теперь указывает на x
*
):fmt.Println(*ptr) // Выведет 42 (значение по адресу)
*ptr = 100 // Изменяем значение x через указатель
var p *int // nil по умолчанию
if p != nil {
*p = 1 // Опасно! Разыменование nil указателя приведет к панике
}
Создается копия значения:
func modifyValue(val int) {
val = 100 // Изменяется только копия
}
x := 42
modifyValue(x)
fmt.Println(x) // 42 (оригинал не изменился)
Передается ссылка на оригинальную переменную:
func modifyPointer(ptr *int) {
*ptr = 100 // Изменяем оригинальное значение
}
x := 42
modifyPointer(&x)
fmt.Println(x) // 100 (оригинал изменился)
Некоторые типы в Go уже содержат указатели внутри и ведут себя особым образом:
func modifySlice(s []int) {
s[0] = 100 // Изменяет оригинальный массив
}
nums := []int{1, 2, 3}
modifySlice(nums)
fmt.Println(nums) // [100 2 3]
func modifyMap(m map[string]int) {
m["key"] = 100
}
data := map[string]int{"key": 1}
modifyMap(data)
fmt.Println(data) // map[key:100]
func resetToZero(ptr *int) {
*ptr = 0
}
value := 5
resetToZero(&value)
type BigStruct struct { /* много полей */ }
func processBig(b *BigStruct) {
// Работаем с оригинальной структурой
}
func (u *User) Save() error {
if u == nil {
return errors.New("nil user")
}
// ...
}
Методы могут быть привязаны как к значению, так и к указателю:
type Counter struct {
value int
}
// По значению (работает с копией)
func (c Counter) Increment() {
c.value++ // Не изменяет оригинал
}
// По указателю (работает с оригиналом)
func (c *Counter) IncrementPtr() {
c.value++ // Изменяет оригинал
}
Можно возвращать указатели на локальные переменные (Go перемещает их в кучу):
func createUser() *User {
return &User{Name: "Alice"} // Безопасно
}
Обычно не нужны, но могут быть полезны для optional-значений:
func printInt(ptr *int) {
if ptr != nil {
fmt.Println(*ptr)
} else {
fmt.Println("nil")
}
}
var p *int
*p = 1 // panic: runtime error
// Плохо в C, но нормально в Go:
func foo() *int {
x := 42
return &x // В Go это безопасно
}
u := User{}
u.Increment() // Не изменяет u
u.IncrementPtr() // Изменяет u
&
и *
Правильное использование указателей — ключ к эффективному и безопасному коду на Go.