Указатели

Когда мы вызываем функцию с аргументами, аргументы копируются в функцию:

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, то читаем это так: «Хранимint0 в памяти, на которую указывает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).

results matching ""

    No results matching ""