Зарегистрируйтесь, чтобы продолжить обучение

Указатели Основы Go

Go — язык с передачей аргументов по значению. Это значит, что при передаче переменной в функцию создаётся её копия, и любые изменения внутри функции не затрагивают оригинал. Но если нужно изменить переменную изнутри функции, используется указатель.

Передача по значению

func main() {
    x := 5
    change(x)
    fmt.Println(x) // 5 — не изменилось
}

func change(n int) {
    n = 10
}

В этом примере переменная x передаётся по значению. Функция получает копию значения, и изменение n не влияет на x.

Передача указателя

func main() {
    x := 5
    change(&x)
    // изменилось
    fmt.Println(x) // => 10
}

func change(number *int) {
    *number = 10
}

Здесь мы передаём в функцию не значение, а указатель на переменную x. Оператор * используется для получения значения по адресу (разыменования). Изменение *number внутри функции приводит к изменению исходной переменной.

Если присвоить без разыменования внутри функции, то оригинальная переменная x не изменится.

func change(number *int) {
    number = 10 // не изменит оригинальную переменную
}

Как это работает

В Go всё передаётся по значению. Но если вы передаёте указатель, то копируется сам указатель (то есть адрес), и обе переменные указывают на одну и ту же область памяти.

x := 42
ptr := &x         // адрес переменной
fmt.Println(ptr)  // например, 0xc00001a0a8
fmt.Println(*ptr) // 42 — значение по адресу

Пример со структурой

Указатели особенно полезны при работе со структурами. Структуры могут содержать много полей, и копирование каждой из них при передаче в функцию может быть неэффективным. Кроме того, иногда нужно изменить структуру внутри функции.

Передача структуры по значению:

type User struct {
    Name string
}

func rename(user User) {
    user.Name = "Алиса"
}

func main() {
    u := User{Name: "Боб"}
    rename(u)
    // Имя не изменилось
    fmt.Println(u.Name) // => Боб
}

В этом примере структура User передаётся по значению. Функция rename() работает с копией, а не с оригиналом.

Передача указателя на структуру:

func rename(user *User) {
    user.Name = "Алиса"
}

func main() {
    u := User{Name: "Боб"}
    rename(&u)
    // Имя изменилось
    fmt.Println(u.Name) // => Алиса
}

Теперь в функцию передаётся указатель на User. Мы получаем доступ к оригинальной структуре и можем её изменить. Таким образом мы получаем привычное поведение для других языков, где составные данные передаются по ссылкам.

Go также позволяет обращаться к полям структуры через указатель без явного разыменования:

user.Name = "..."    // работает
(*user).Name = "..." // тоже работает

Это сделано для удобства — компилятор сам вставляет *, если это безопасно.

Когда использовать указатели

  • Когда нужно изменить значение переменной или структуры внутри функции
  • Когда объект большой, и копировать его неэффективно

Дополнительные материалы

  1. Address operators
  2. Указатели в Go
  3. Еще об указателях

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff