Menu
Coddy logo textTech

Указатели на структуры

Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 20 из 107.

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

Вы можете создать указатель на структуру, используя оператор & для существующей структуры или используя оператор взятия адреса напрямую с литералом структуры.

type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}
ptr := &p  // указатель на существующую структуру

// Или создайте напрямую
ptr2 := &Person{Name: "Bob", Age: 25}

Go предоставляет удобное сокращение при доступе к полям структуры через указатель. Вместо того чтобы писать (*ptr).Name, вы можете просто написать ptr.Name. Go автоматически разыменовывает указатель за вас.

ptr := &Person{Name: "Alice", Age: 30}

// Оба варианта работают одинаково
fmt.Println((*ptr).Name)  // явное разыменование
fmt.Println(ptr.Name)     // автоматическое разыменование

Это автоматическое разыменование также применяется при изменении полей. Изменения через указатель влияют на исходную структуру.

p := Person{Name: "Alice", Age: 30}
ptr := &p

ptr.Age = 31           // изменяет оригинал
fmt.Println(p.Age)     // 31

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

challenge icon

Задание

Легко

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

Вы организуете свой код в двух файлах:

  • account.go: Определите структуру BankAccount с полями Owner (string), Balance (float64) и AccountNumber (string). Создайте функцию с именем NewAccount, которая принимает имя владельца и номер счета в качестве параметров и возвращает указатель на новый BankAccount с начальным балансом 0. Также создайте функцию Deposit, которая принимает указатель *BankAccount и сумму, а затем добавляет эту сумму к балансу счета через указатель.
  • main.go: Считайте информацию о счете из входных данных, создайте счет с помощью вашей функции-конструктора (которая возвращает указатель), внесите депозит, используя указатель, и выведите данные счета, чтобы подтвердить, что баланс был изменен.

Будут предоставлены следующие входные данные:

  • Строка 1: Имя владельца
  • Строка 2: Номер счета
  • Строка 3: Сумма депозита (float)

Формат вывода:

Account: [AccountNumber]
Owner: [Owner]
Balance: [Balance]

Например, для John Smith, ACC-12345 и 500.50 ваш вывод должен быть следующим:

Account: ACC-12345
Owner: John Smith
Balance: 500.50

Помните, что ваша функция NewAccount должна возвращать *BankAccount, используя оператор &, а функция Deposit может изменять баланс напрямую через указатель, используя автоматическое разыменование в Go (например, account.Balance вместо (*account).Balance).

Шпаргалка

Указатель на структуру хранит адрес памяти экземпляра структуры. Создавайте указатели на структуры с помощью оператора &:

type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}
ptr := &p  // указатель на существующую структуру

// Или создайте напрямую
ptr2 := &Person{Name: "Bob", Age: 25}

Go обеспечивает автоматическое разыменование при доступе к полям структуры через указатель. Вместо (*ptr).Name вы можете писать ptr.Name:

ptr := &Person{Name: "Alice", Age: 30}

// Оба варианта работают одинаково
fmt.Println((*ptr).Name)  // явное разыменование
fmt.Println(ptr.Name)     // автоматическое разыменование

Изменения через указатель влияют на исходную структуру:

p := Person{Name: "Alice", Age: 30}
ptr := &p

ptr.Age = 31           // изменяет оригинал
fmt.Println(p.Age)     // 31

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

Попробуйте сами

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Прочитать имя владельца
	ownerName, _ := reader.ReadString('\n')
	ownerName = strings.TrimSpace(ownerName)
	
	// Прочитать номер счета
	accountNumber, _ := reader.ReadString('\n')
	accountNumber = strings.TrimSpace(accountNumber)
	
	// Прочитать сумму депозита
	amountStr, _ := reader.ReadString('\n')
	amountStr = strings.TrimSpace(amountStr)
	amount, _ := strconv.ParseFloat(amountStr, 64)
	
	// TODO: Создать новый аккаунт, используя функцию NewAccount (возвращает указатель)
	
	// TODO: Внести депозит, используя функцию Deposit с указателем на аккаунт
	
	// TODO: Вывести данные аккаунта в требуемом формате:
	// Account: [AccountNumber]
	// Owner: [Owner]
	// Balance: [Balance]
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование