Как указатели помогают оптимизировать распределение памяти? Ответ в данном уроке.
В предыдущем уроке мы разобрались со структурами, и как они помогают писать понятные программы на языке Go.
Применение структур вполне понятно, чего не скажешь про указатели. Тем не менее указатели важная тема и профессиональному разработчику без них не обойтись.
В данном уроке будут допускаться значительные упрощения при описании определенных характеристик работы программы. Упрощение позволит получить первоначальную информацию и дальше над ней надстраивать дополнительный материал.
Чтобы понять, что такое указатель и зачем он нужен, давайте рассмотрим пример.
Адресация на складе
Допустим, что у нас есть складское помещение, где на разных стеллажах лежат товары. Каждый стеллаж находится в определенном ряду. Ряд состоит из множества стеллажей, которые располагаются друг за другом. Каждый ряд и стеллаж обозначены двумя значениями, которые однозначно определяют их расположение в пространстве склада. На стеллаже есть ячейки для хранения товара. Ячейки имеют разные размеры для хранения товаров с различными габаритами. Для быстрого поиска товара важно быстро найти место, где находится ячейка. Использование адресации в этом очень помогает и значительно облегчает процесс поиска.
Переменные в программе похожи на товары на складе. Они хранятся в оперативной памяти компьютера.
Для поиска места, где хранится значение переменной, также используются адресация. Мы не будем детально описать устройство памяти. Но важно понимать, что у каждой переменной есть адрес.
Указатель
Указатель — это адрес ячейки памяти, где хранится значение переменной.
Например, мы объявляем переменную:
var a int
Ее значение находится в оперативной памяти компьютера. В нашем случае это значение 0.
В оперативной памяти (RAM) для переменной a резервируется необходимый объем для хранения целого числа. Он равен 64 бит. Здесь мы предполагаем, что тип данных int занимает 64 бита в памяти.
Теперь, предположим, что нам необходимо из двух разных функций изменить значение этой переменной. Причем, сделать это нужно в цикле, состоящего из 1 миллиона шагов.
Не зная про существование указателей, мы напишем примерно такой код:
В этом примере не так важно, что делают функции foo и bar. При вызове функции foo и bar будет каждый раз создаваться новая переменная a, время жизни которой ограничено временем выполнения функции.
В языке Go значение аргумента в функцию передается путем создания новой области памяти и копированием значения. Такой вариант передачи аргументов называется — передача по значению.
Таким образом, при выполнении каждой итерации будет создана переменная, которая занимает объем памяти равный 64 бита. Операция создания новой переменной и заполнение ее значением это не бесплатная процедура. На любую операцию уходит ресурс процессора. Как и любой ресурс он ограничен в объеме.
Давайте представим, что мы ограничены в ресурсах и вычислительных мощностям системы на которой запускается наша программа.
В нашем примере память и ресурс процессора будет расходоваться не оптимально.
На каждой итерации цикла мы создаем область памяти размеров 64 бита и после выполнения функции эта память освобождается.
С помощью указателей можно обойтись без создания новой переменной. Вместо самого значения в функции будем передавать указатель на переменную.
Обратите внимание на функцию foo и bar. Мы изменили тип аргумента на *int.
Этот тип называется указателем на int.
Важно отметить, что для получения значения переменной с помощью указателя, используется операция звездочка - *.
Не стоит путать ее с операцией умножения.
На 19 и 23 строке мы передаем указатель с помощью операции & - получения указателя на переменную.
Теперь, вместо создания на каждой итерации новых переменных, мы используем одну общую ячейку памяти, с которой работают функции foo и bar.
Указатели позволяют оптимизировать выделение памяти и исключить дорогостоящие операции копирования аргументов функции. Но использование указателей стоит отложить до момента, когда их применение будет оправданно.
Их использование может также ухудшить работу программы.
На вопрос - в каком случае использовать указатели, а когда обычные переменные, нет однозначного ответа. Выбор нужно основывать на количественных показателях программы, полученных с помощью запуска тестов (benchmark).
Практика
Переходи по ссылке и пройди практику по данному уроку в обучающей онлайн — платформе Stepik.
В следующем уроке рассмотрим методы структур и применение указателей вместе со структурами.