Поймите указатели в Go меньше чем за 800 слов или вернём деньги
- вторник, 14 октября 2025 г. в 00:00:11
Оригинал: Understand Go pointers in less than 800 words or your money back
Автор: Дэйв Чейни
Дата: 26 апреля 2017 г.
Проще говоря, указатель — это значение, которое указывает на адрес другого значения. Это определение из учебников, но если вы пришли из языка, который не позволяет работать с адресами переменных, эта фраза может показаться магической.
Давайте разберемся.
Компьютерная память (RAM) — это последовательность ячеек, расположенных друг за другом. Каждая ячейка обозначена уникальным числом (адресом), который увеличивается последовательно: это и есть адрес этой ячейки.
Каждая ячейка хранит одно значение. Если вы знаете адрес ячейки, вы можете считать из нее содержимое или записать туда новое. Всё, что делает процессор, — это извлекает и помещает значения в эти ячейки памяти.
Допустим, нам нужно написать программу, которая берет значение из ячейки с адресом 200, умножает его на 3 и помещает результат в ячейку 201. Это можно записать на псевдокоде так:
получить значение по адресу 200 и поместить его в процессор
умножить его на 3
записать результат в адрес 201
Ранние программы так и писались: программист вел список адресов используемой памяти, кто и когда их использует, что там хранится. Но это было утомительно и чревато ошибками: все значения должны были иметь свой уникальный адрес, и нельзя было динамически выделять память под переменные во время выполнения.
Для решения этой проблемы появилось понятие переменной. Переменная — это просто удобный буквенно-цифровой псевдоним для адреса в памяти; метка или никнейм.
Теперь вместо работы напрямую с адресами, мы можем использовать имена переменных. Приведённый выше код можно теперь записать как:
• получить значение переменной a
и поместить в процессор
• умножить на 3
• записать в переменную b
Это та же самая программа, но гораздо удобнее: нам больше не нужно следить за адресами — этим займется компилятор.
Теперь мы можем написать:
var a = 6
var b = a * 3
И компилятор выделит уникальные адреса под a
и b
автоматически.
Теперь, когда мы знаем, что память — это просто последовательные ячейки, а переменные — имена этих ячеек, возникает вопрос: что такое указатель?
Указатель — это значение, которое хранит адрес другой переменной.
Указатель указывает на адрес переменной, так же как переменная отвечает за адрес, по которому хранится её значение.
Посмотрим на пример:
func main() {
a := 200
b := &a
*b++
fmt.Println(a)
}
В первой строке функции main
мы объявляем переменную a
и присваиваем ей значение 200.
Далее объявляется переменная b
и ей присваивается адрес a
. Мы не знаем, где в памяти хранится a
, но можем сохранить её адрес в b
.
Третья строка, вероятно, самая запутанная. b
содержит адрес переменной a
, но мы хотим увеличить значение, хранящееся в a
. Для этого нужно разыменовать b
, то есть пройти по адресу из b
и получить содержимое из a
.
Затем мы увеличиваем это значение на 1 и снова записываем по адресу, хранящемуся в b
.
Последняя строка выводит значение a
— оно стало 201.
Если вы пришли из языка без явных указателей или где каждая переменная по сути является указателем — не переживайте. На формирование ментальной модели переменных и указателей уходит время и практика — просто запомните:
Указатель — это значение, которое указывает на адрес другой переменной.