Указатели
Когда мы вызываем функцию с аргументами, аргументы копируются в функцию:
func zero(x int) {
x = 0
}
func main() {
x := 5
zero(x)
fmt.Println(x) // x всё еще равен 5
}
В этой программе функцияzero
не изменяет оригинальную переменнуюx
из функцииmain
. Но что если мы хотим её изменить? Один из способов сделать это — использовать специальный тип данных — указатель:
func zero(xPtr *int) {
*xPtr = 0
}
func main() {
x := 5
zero(&x)
fmt.Println(x) // x is 0
}
Указатели указывают (прошу прощения за тавтологию) на участок в памяти, где хранится значение. Используя указатель (*int
) в функцииzero
, мы можем изменить значение оригинальной переменной.
Операторы * и &
В Go указатели представлены через оператор * (звёздочка), за которым следует тип хранимого значения. В функцииzeroxPtr
является указателем наint
.
*
также используется для «разыменовывания» указателей. Когда мы пишем*xPtr = 0
, то читаем это так: «Хранимint
0 в памяти, на которую указываетxPtr
». Если вместо этого мы попробуем написатьxPtr = 0
, то получим ошибку компиляции, потому чтоxPtr
имеет тип неint
, а*int
. Соответственно, ему может быть присвоен только другой*int
.
Также существует оператор&
, который используется для получения адреса переменной.&x
вернет*int
(указатель наint
) потому чтоx
имеет типint
. Теперь мы можем изменять оригинальную переменную.&x
в функцииmain
иxPtr
в функцииzero
указывают на один и тот же участок в памяти.
Оператор new
Другой способ получить указатель — использовать встроенную функциюnew
:
func one(xPtr *int) {
*xPtr = 1
}
func main() {
xPtr := new(int)
one(xPtr)
fmt.Println(*xPtr) // x is 1
}
Функцияnew
принимает аргументом тип, выделяет для него память и возвращает указатель на эту память.
В некоторых языках программирования есть существенная разница между использованиемnew
и&
, и в них нужно удалять всё, что было создано с помощьюnew
. Go не такой - Go хороший. Go — язык с автоматической сборкой мусора. Это означает, что область памяти очищается автоматически, когда на неё не остаётся ссылок.
Указатели редко используются в Go для встроенных типов, но они будут часто фигурировать в следующей главе (они чрезвычайно полезны при работе со структурами).
Задачи
Как получить адрес переменной?
Как присвоить значение указателю?
Как создать новый указатель?
Какое будет значение у переменной
x
после выполнения программы:func square(x *float64) { *x = *x * *x } func main() { x := 1.5 square(&x) }
Напишите программу, которая меняет местами два числа (
x := 1; y := 2; swap(&x, &y)
должно датьx=2
иy=1
).