Указатели

Указатели наглядно: что такое & и *, зачем они нужны и где 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.
Проверьте себя
1. Что делает оператор & перед переменной?
AЧитает значение по адресу
BВозвращает адрес переменной
CСоздаёт копию
DУдаляет переменную
2. Почему функция incValue(n int) не меняет оригинал, а incPointer(n *int) меняет?
AGo всегда передаёт по ссылке
BАргументы передаются по значению (копией); указатель даёт доступ к оригиналу
Cint нельзя менять
DincValue содержит ошибку
3. Что из этого верно про указатели в Go?
AПоддерживается арифметика указателей как в C
BАрифметики указателей нет ради безопасности памяти
CУказатель нельзя передать в функцию
DУказатели обязательны для любых типов
Поддержать проект