Введение

Указатели — фундаментальное понятие в программировании. Они позволяют вам передавать адреса памяти значениям и записям в вашей программе. Указатели особенно полезны в Go, поскольку они позволяют манипулировать данными непосредственно в памяти, что может привести к значительному повышению производительности.

В этой статье мы рассмотрим, как работают указатели в Go и чем они отличаются от обычных значений. Мы рассмотрим две функции, zeroval и zeroptr, чтобы проиллюстрировать различия между этими двумя типами. Мы также увидим, как печатать указатели и изменять значения в памяти с помощью указателей.

Передача по значению против передачи по ссылке

В Go вы можете передавать аргументы функциям либо по значению, либо по ссылке. Когда вы передаете аргумент по значению, создается копия значения и передается в функцию. Исходное значение не изменяется функцией. Когда вы передаете аргумент по ссылке, функция получает адрес памяти или указатель на исходное значение. Это означает, что функция может изменить исходное значение.

Давайте взглянем на две функции, zeroval и zeroptr, чтобы увидеть, как эти концепции работают на практике:

func zeroval(ival int) {
    ival = 0
}

func zeroptr(iptr *int) {
    *iptr = 0
}

zeroval принимает параметр int, поэтому аргументы будут передаваться ему по значению. Это означает, что zeroval получит копию ival, отличную от копии в вызывающей функции. zeroptr, с другой стороны, принимает параметр *int, который является указателем на int. Это означает, что он принимает адрес памяти переменной int. Затем код *iptr в теле функции разыменовывает указатель из его адреса памяти в текущее значение по этому адресу. Присвоение значения разыменованному указателю изменяет значение по указанному адресу.

Давайте посмотрим на эти функции в действии:

i := 1
fmt.Println("initial:", i)

zeroval(i)
fmt.Println("zeroval:", i)

zeroptr(&i)
fmt.Println("zeroptr:", i)

В первой строке мы объявляем переменную i и инициализируем ее значением 1. Затем мы вызываем zeroval с i в качестве аргумента. Это должно установить i в 0, верно? Давай выясним:

initial: 1
zeroval: 1

Как видите, zeroval не изменило значение i. Это связано с тем, что zeroval получил копию значения i, а не адрес памяти самого i. Поэтому любые изменения, сделанные внутри zeroval, не отражаются в исходной переменной.

Теперь попробуем вызвать zeroptr с i в качестве аргумента:

zeroptr(&i)
fmt.Println("zeroptr:", i)

На этот раз мы передаем адрес памяти i в zeroptr. Затем функция разыменовывает указатель и устанавливает значение по этому адресу памяти в 0. Давайте посмотрим, сработало ли это:

zeroptr: 0

Успех! zeroptr смог изменить значение i, так как получил указатель на исходную переменную.

Печать указателей

Печатать указатели в Go очень просто. Вы можете использовать оператор &, чтобы получить адрес памяти переменной. Вот пример:

fmt.Println("pointer:", &i)

Это напечатает адрес памяти i. Если вы запустите полную программу, вывод должен выглядеть так:

initial: 1
zeroval: 1
zeroptr: 0
pointer: 0xc0000180d0

Как видите, адрес памяти i печатается в конце программы. На вашем компьютере адрес может быть другим.

Заключение

В этой статье мы рассмотрели основы указателей в Go. Мы видели, как указатели позволяют вам передавать адреса памяти значениям и записям в вашей программе и чем они отличаются от обычных значений. Мы также видели, как печатать указатели и изменять значения в памяти с помощью указателей.

Поначалу указатели могут быть немного сложными для понимания, но как только вы освоите их, они могут стать мощным инструментом в вашем арсенале программирования. Независимо от того, создаете ли вы высокопроизводительное приложение или просто пытаетесь сделать свой код немного более эффективным, указатели, безусловно, заслуживают изучения.

Удачного кодирования!