cpp

Указатели

Указатель — это переменная, которая предназначена для хранения адреса. Объявление указателя имеет следующий формат:

var <Переменная> *<Тип>[ = <Значение>]

Пример объявления указателя на тип int:

var p *int
fmt.Println(p)                  // <nil>
// Вывод адреса
fmt.Printf("%p\n", p)           // 0x0
// Вывод типа
fmt.Printf("%T\n", p)           // *int
// Вывод размера
fmt.Println(unsafe.Sizeof(p))   // 8

Если при объявлении указателю не присвоено начальное значение, то переменная автоматически получит значение nil. Согласно соглашению, указатели, которые не на что не указывают, должны иметь значение nil.

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

var x int = 10
var p *int = nil
p = &x
fmt.Printf("%p\n", p)           // 0xc000012088
fmt.Printf("%p\n", &x)          // 0xc000012088

Чтобы получить или изменить значение, расположенное по адресу на который ссылается указатель, необходимо выполнить операцию разыменования указателя. Для этого перед названием переменной указывается оператор *. Пример:

var x int = 10
var p *int = &x
fmt.Println(*p)                 // 10
*p = *p + 20
fmt.Println(*p)                 // 30

Основные операции с указателями приведены в листинге 2.1.

Листинг 2.1. Указатели

package main

import "fmt"

func main() {
   var x int = 10
   var p *int = nil
   // Присваивание указателю адреса переменной x
   p = &x
   // Вывод адреса
   fmt.Println(p)                 // 0xc000012088
   fmt.Printf("%p\n", p)          // 0xc000012088
   // Вывод значения
   fmt.Println(*p)                // 10
}

Значение одного указателя можно присвоить другому указателю. При этом важно учитывать, что типы указателей должны совпадать. Пример:

var x int = 10
var p1, p2 *int
p1 = &x
p2 = p1                    // Копирование адреса переменной x
fmt.Println(*p2)           // 10
*p2 = 40                   // Изменение значения в переменной x
fmt.Println(*p1)           // 40
fmt.Println(x)             // 40

В этом примере мы просто скопировали адрес переменной x из одного указателя в другой. Помимо копирования адреса можно создать указатель на указатель. Для этого при объявлении перед названием переменной указываются два оператора *:

var <Переменная> **<Тип>[ = <Значение>]

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

var x int = 10
var p1 *int
var p2 **int
p1 = &x
p2 = &p1                   // Указатель на указатель
fmt.Println(**p2)          // 10
**p2 = 40                  // Изменение значения в переменной x
fmt.Println(*p1)           // 40
fmt.Println(x)             // 40

При каждом вложении указывается дополнительная звездочка:

var x int = 10
var p1 *int
var p2 **int
var p3 ***int
p1 = &x
p2 = &p1
p3 = &p2
fmt.Println(***p3)         // 10
***p3 = 40                 // Изменение значения в переменной x
fmt.Println(**p2)          // 40
fmt.Println(*p1)           // 40
fmt.Println(x)             // 40

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

Указатели часто используются при передаче параметров в функцию. По умолчанию в функцию передается копия значения переменной. Если мы в этом случае изменим значение внутри функции, то это действие не затронет значения внешней переменной. Чтобы иметь возможность изменять значение внешней переменой, параметр функции объявляется как указатель, а при вызове функции передается адрес переменной (листинг 2.2).

Листинг 2.2. Передача параметров в функцию

package main

import "fmt"

func main() {
   var y int = 10
   func1(y)                   // Передаем копию значения
   fmt.Println(y)             // 10 (значение не изменилось!!!)
   
   func2(&y)                  // Передаем адрес, а не значение
   fmt.Println(y)             // 20 (значение изменилось!!!)
}
func func1(x int) {
   x = x * 2                  // Значение нигде не сохраняется
}
func func2(x *int) {
   *x = *x * 2
}

При использовании функции func1() передача параметра осуществляется по значению (применяется по умолчанию). При этом создается копия значения и все операции производятся с этой копией. Так как локальные переменные видны только внутри тела функции, после завершения выполнения функции копия удаляется. Любые изменения значения копии не затронут значения оригинала.

При использовании функции func2() мы передаем адрес переменной:

func2(&y)

Внутри функции адрес присваивается указателю. Используя операцию разыменования указателя можно изменить значение самой переменной, а не значение копии. Достигается это с помощью следующей инструкции:

*x = *x * 2

Учебник Go (Golang)
Учебник Go (Golang) в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов

cpp