Для работы с указателями существует два оператора — амперсанд & и звёздочка *.
Если перед именем переменной стоит амперсанд, значит мы хотим получить адрес этой переменной. Звездочка - это оператор разыменовывания.
В примере указатель ptr не содержит значение переменной p типа int, а содержит только ее адрес.
Адрес в памяти представляет собой шестнадцатеричное число, например, 0x1400009c018. Каждая программа при запуске создается в собственном пространстве памяти, указатель будет разным при каждом запуске, поэтому, если вы запустите код из примера, то адрес у вас будет другим.
Изменение значения по указателю
Переменная ptr указывает на адрес переменной str. Это означает, что если мы изменим значение, на которое указывает переменная ptr, мы также изменим значение переменной str.
В конце мы установили для переменной-указателя ptr значение nil, тем самым освободим область памяти, выделенную под переменную ptr. Значение переменной str не изменилось.
Передача по значению и по ссылке
В Go все аргументы функций по умолчанию передаются по значению. Передается копия значения и любые изменения аргумента в этой функции влияют только на эту функцию, но не на источник.
Если передавать аргумент в функцию по ссылке, то можно изменить значение внутри функции и тем самым изменить значение исходной переменной, которая была передана.
Если надо, чтобы функция, куда вы передаете переменную, могла изменять эту переменную, вы должны передавать ее как указатель.
Нулевое значение и функция new
Для указателей нулевым значением является nil, что означает, что указатель не указывает ни на какой объект в памяти.
Еще один способ объявления указателей использования функции new. Она используется для выделения памяти для переменной и возвращает указатель на эту переменную. Указатель, возвращаемый функцией new, указывает на нулевое значение типа, для которого была выделена память.
Хорошей практикой является проверка аргумента указателя на равенство nil. Это защитит программу от генерации паники.
Ресиверы (приемники) методов, как указатели
В Go, методы могут быть связаны как со значениями структур, так и с указателями на структуры. Это определяет, как метод будет получать доступ к экземпляру структуры и какие операции он сможет выполнять.
Методы с приемниками-значениями
Когда метод имеет приемник-значение, он работает с копией. Изменения, вносимые в поля структуры внутри метода, не затрагивают оригинальную структуру.
Метод Scale имеет приемник-значение, поэтому, когда он вызывается, используется копия v, и изменения в Scale не влияют на исходное значение v.
Методы с приемниками-указателями
Если метод имеет приемник-указатель, он может изменять поля структуры, на которую указывает приемник, поскольку имеет прямой доступ к этой памяти.
В этом примере метод Scale имеет приемник-указатель, и изменения, которые он вносит в v, влияют на оригинальный экземпляр структуры, так как v фактически указывает на место в памяти, где хранится Vertex.
Примечание по синтаксису
Можно вызвать метод с приемником-указателем на значение, и компилятор автоматически возьмет адрес этого значения.
Такое поведение очень удобно, поскольку позволяет избегать явного разыменования или получения адреса значения при вызове методов.
Арифметика указателей
В Go, в отличие от других языков, таких как C++, арифметика указателей не поддерживается напрямую. К примеру, нельзя прибавить или вычесть числа непосредственно у указателя, перемещая его вдоль блока памяти.
Отсутствие прямого доступа к арифметике указателей способствует безопасности памяти в Go, так как исключает класс ошибок, связанных с выходом за границы массива, неправильным обращением к памяти и другими подобными проблемами.
Если нужно выполнить операции, похожие на арифметику указателей, то можно попробовать реализовать подобное на слайсах: