Найти в Дзене
Golang с 0

Go (Golang) c нуля - Урок 10. Указатели

Оглавление

Как указатели помогают оптимизировать распределение памяти? Ответ в данном уроке.

Уроки по Go | Golang с 0 | Дзен

В предыдущем уроке мы разобрались со структурами, и как они помогают писать понятные программы на языке Go.

Применение структур вполне понятно, чего не скажешь про указатели. Тем не менее указатели важная тема и профессиональному разработчику без них не обойтись.

В данном уроке будут допускаться значительные упрощения при описании определенных характеристик работы программы. Упрощение позволит получить первоначальную информацию и дальше над ней надстраивать дополнительный материал.

Чтобы понять, что такое указатель и зачем он нужен, давайте рассмотрим пример.

Адресация на складе

Допустим, что у нас есть складское помещение, где на разных стеллажах лежат товары. Каждый стеллаж находится в определенном ряду. Ряд состоит из множества стеллажей, которые располагаются друг за другом. Каждый ряд и стеллаж обозначены двумя значениями, которые однозначно определяют их расположение в пространстве склада. На стеллаже есть ячейки для хранения товара. Ячейки имеют разные размеры для хранения товаров с различными габаритами. Для быстрого поиска товара важно быстро найти место, где находится ячейка. Использование адресации в этом очень помогает и значительно облегчает процесс поиска.

Схематическое расположения стеллажей на складе и их адресов
Схематическое расположения стеллажей на складе и их адресов

Переменные в программе похожи на товары на складе. Они хранятся в оперативной памяти компьютера.

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

Указатель

Указатель — это адрес ячейки памяти, где хранится значение переменной.

Например, мы объявляем переменную:

var a int

Ее значение находится в оперативной памяти компьютера. В нашем случае это значение 0.

В оперативной памяти (RAM) для переменной a резервируется необходимый объем для хранения целого числа. Он равен 64 бит. Здесь мы предполагаем, что тип данных int занимает 64 бита в памяти.

Теперь, предположим, что нам необходимо из двух разных функций изменить значение этой переменной. Причем, сделать это нужно в цикле, состоящего из 1 миллиона шагов.

Не зная про существование указателей, мы напишем примерно такой код:

-3

В этом примере не так важно, что делают функции foo и bar. При вызове функции foo и bar будет каждый раз создаваться новая переменная a, время жизни которой ограничено временем выполнения функции.

В языке Go значение аргумента в функцию передается путем создания новой области памяти и копированием значения. Такой вариант передачи аргументов называется — передача по значению.

Таким образом, при выполнении каждой итерации будет создана переменная, которая занимает объем памяти равный 64 бита. Операция создания новой переменной и заполнение ее значением это не бесплатная процедура. На любую операцию уходит ресурс процессора. Как и любой ресурс он ограничен в объеме.

Давайте представим, что мы ограничены в ресурсах и вычислительных мощностям системы на которой запускается наша программа.

В нашем примере память и ресурс процессора будет расходоваться не оптимально.

На каждой итерации цикла мы создаем область памяти размеров 64 бита и после выполнения функции эта память освобождается.

С помощью указателей можно обойтись без создания новой переменной. Вместо самого значения в функции будем передавать указатель на переменную.

Передача указателя вместо значения переменной a
Передача указателя вместо значения переменной a
The Go Play Space

Обратите внимание на функцию foo и bar. Мы изменили тип аргумента на *int.

Этот тип называется указателем на int.

Важно отметить, что для получения значения переменной с помощью указателя, используется операция звездочка - *.

Не стоит путать ее с операцией умножения.

На 19 и 23 строке мы передаем указатель с помощью операции & - получения указателя на переменную.

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

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

Их использование может также ухудшить работу программы.

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

Практика

Переходи по ссылке и пройди практику по данному уроку в обучающей онлайн — платформе Stepik.

Указатели

В следующем уроке рассмотрим методы структур и применение указателей вместе со структурами.

До скорых встреч!