Указатели представляют собой объекты, значением которых служат адреса других объектов (например, переменных).
Указатель определяется как обычная переменная, только перед типом данных ставится символ звездочки *. Например, определение указателя на объект типа int:
var p *int
Этому указателю можно присвоить адрес переменной типа int. Для получения адреса применяется операция &, после которой указывается
имя переменной (&x
).
package main import "fmt" func main() { var x int = 4 // определяем переменную var p *int // определяем указатель p = &x // указатель получает адрес переменной fmt.Println(p) // значение самого указателя - адрес переменной x }
Здесь указатель p хранит адрес переменной x. Что важно, переменная x имеет тип int, и указатель p указывает именно на объект типа int. То есть должно быть соответствие по типу. И если мы попробуем вывести адрес переменной на консоль, то увидим, что он представляет шестнадцатеричное значение:
0xc0420120a0
В каждом отдельном случае адрес может отличаться, но к примеру, в моем случае машинный адрес переменной x - 0xc0420120a0. То есть в памяти компьютера есть адрес 0xc0420120a0, по которому располагается переменная x.
По адресу, который хранит указатель, мы получить значение переменной x. Для этого применяется операция * или операция разыменования. Результатом этой операции является значение переменной, на которую указывает указатель. Применим данную операцию и получим значение переменной x:
package main import "fmt" func main() { var x int = 4 var p *int = &x // указатель получает адрес переменной fmt.Println("Address:", p) // значение указателя - адрес переменной x fmt.Println("Value:", *p) // значение переменной x }
Консольный вывод:
Address: 0xc0420c058 Value: 4
И также используя указатель, мы можем менять значение по адресу, который хранится в указателе:
var x int = 4 var p *int = &x *p = 25 fmt.Println(x) // 25
Для определения указателей можно использовать также сокращенную форму:
f := 2.3 pf := &f fmt.Println("Address:", pf) fmt.Println("Value:", *pf)
Если указателю не присвоен адрес какого-либо объекта, то такой указатель по умолчанию имеет значение nil (по сути отстутствие значения). Если мы попробуем получить значение по такому пустому указателю, то мы столкнемся с ошибкой:
var pf *float64 fmt.Println("Value:", *pf) // ! ошибка, указатель не указывает на какой-либо объект
Поэтому при работе с указателями иногда бывает целесообразано проверять на значение nil:
var pf *float64 if pf != nil{ fmt.Println("Value:", *pf) }
Переменная представляет именованный объект в памяти. Язык Go также позволяет создавать безымянные объекты - они также размещаются в памяти, но не имеют имени как переменные. Для этого применяется функция new(type). В эту функцию передается тип, объект которого надо создать. Функция возвращает указатель на созданный объект:
package main import "fmt" func main() { p := new(int) fmt.Println("Value:", *p) // Value: 0 - значение по умолчанию *p = 8 // изменяем значение fmt.Println("Value:", *p) // Value: 8 }
В данном случае указатель p будет иметь тип *int
, поскольку он указывает на объект типа int. Создаваемый объект имеет значение по умолчанию (для типа int
это число 0).
Объект, созданный с помощью функции new, ничем не отличается от обычной переменной. Единственное что, чтобы обратиться к этому объекту - получить или изменить его адрес, необходимо использовать указатель.