Golang: Указатели
Go — язык с передачей аргументов по значению. Это значит, что при передаче переменной в функцию создаётся её копия, и любые изменения внутри функции не затрагивают оригинал. Но если нужно изменить переменную изнутри функции, используется указатель.
Передача по значению
func main() {
x := 5
change(x)
fmt.Println(x) // 5 — не изменилось
}
func change(n int) {
n = 10
}В этом примере переменная x передаётся по значению. Функция получает копию значения, и изменение n не влияет на x.
Передача указателя
func main() {
x := 5
change(&x)
// изменилось
fmt.Println(x) // => 10
}
func change(number *int) {
*number = 10
}Здесь мы передаём в функцию не значение, а указатель на переменную x. Оператор * используется для получения значения по адресу (разыменования). Изменение *number внутри функции приводит к изменению исходной переменной.
Если присвоить без разыменования внутри функции, то оригинальная переменная x не изменится.
func change(number *int) {
number = 10 // не изменит оригинальную переменную
}Как это работает
В Go всё передаётся по значению. Но если вы передаёте указатель, то копируется сам указатель (то есть адрес), и обе переменные указывают на одну и ту же область памяти.
x := 42
ptr := &x // адрес переменной
fmt.Println(ptr) // например, 0xc00001a0a8
fmt.Println(*ptr) // 42 — значение по адресуПример со структурой
Указатели особенно полезны при работе со структурами. Структуры могут содержать много полей, и копирование каждой из них при передаче в функцию может быть неэффективным. Кроме того, иногда нужно изменить структуру внутри функции.
Передача структуры по значению:
type User struct {
Name string
}
func rename(user User) {
user.Name = "Алиса"
}
func main() {
u := User{Name: "Боб"}
rename(u)
// Имя не изменилось
fmt.Println(u.Name) // => Боб
}В этом примере структура User передаётся по значению. Функция rename() работает с копией, а не с оригиналом.
Передача указателя на структуру:
func rename(user *User) {
user.Name = "Алиса"
}
func main() {
u := User{Name: "Боб"}
rename(&u)
// Имя изменилось
fmt.Println(u.Name) // => Алиса
}Теперь в функцию передаётся указатель на User. Мы получаем доступ к оригинальной структуре и можем её изменить. Таким образом мы получаем привычное поведение для других языков, где составные данные передаются по ссылкам.
Go также позволяет обращаться к полям структуры через указатель без явного разыменования:
user.Name = "..." // работает
(*user).Name = "..." // тоже работаетЭто сделано для удобства — компилятор сам вставляет *, если это безопасно.
Когда использовать указатели
- Когда нужно изменить значение переменной или структуры внутри функции
- Когда объект большой, и копировать его неэффективно
Задание
Создайте структуру Account с полями:
Owner— имя владельца (строка),Balance— баланс (целое число).
Реализуйте функцию Deposit(), которая принимает указатель на Account и сумму пополнения. Функция должна увеличить баланс на указанную сумму.
Пример
acc := Account{Owner: "Alice", Balance: 100}
Deposit(&acc, 50)
fmt.Println(acc.Balance) // 150Полезное
Golang: Указатели
Go — язык с передачей аргументов по значению. Это значит, что при передаче переменной в функцию создаётся её копия, и любые изменения внутри функции не затрагивают оригинал. Но если нужно изменить переменную изнутри функции, используется указатель.
Передача по значению
func main() {
x := 5
change(x)
fmt.Println(x) // 5 — не изменилось
}
func change(n int) {
n = 10
}В этом примере переменная x передаётся по значению. Функция получает копию значения, и изменение n не влияет на x.
Передача указателя
func main() {
x := 5
change(&x)
// изменилось
fmt.Println(x) // => 10
}
func change(number *int) {
*number = 10
}Здесь мы передаём в функцию не значение, а указатель на переменную x. Оператор * используется для получения значения по адресу (разыменования). Изменение *number внутри функции приводит к изменению исходной переменной.
Если присвоить без разыменования внутри функции, то оригинальная переменная x не изменится.
func change(number *int) {
number = 10 // не изменит оригинальную переменную
}Как это работает
В Go всё передаётся по значению. Но если вы передаёте указатель, то копируется сам указатель (то есть адрес), и обе переменные указывают на одну и ту же область памяти.
x := 42
ptr := &x // адрес переменной
fmt.Println(ptr) // например, 0xc00001a0a8
fmt.Println(*ptr) // 42 — значение по адресуПример со структурой
Указатели особенно полезны при работе со структурами. Структуры могут содержать много полей, и копирование каждой из них при передаче в функцию может быть неэффективным. Кроме того, иногда нужно изменить структуру внутри функции.
Передача структуры по значению:
type User struct {
Name string
}
func rename(user User) {
user.Name = "Алиса"
}
func main() {
u := User{Name: "Боб"}
rename(u)
// Имя не изменилось
fmt.Println(u.Name) // => Боб
}В этом примере структура User передаётся по значению. Функция rename() работает с копией, а не с оригиналом.
Передача указателя на структуру:
func rename(user *User) {
user.Name = "Алиса"
}
func main() {
u := User{Name: "Боб"}
rename(&u)
// Имя изменилось
fmt.Println(u.Name) // => Алиса
}Теперь в функцию передаётся указатель на User. Мы получаем доступ к оригинальной структуре и можем её изменить. Таким образом мы получаем привычное поведение для других языков, где составные данные передаются по ссылкам.
Go также позволяет обращаться к полям структуры через указатель без явного разыменования:
user.Name = "..." // работает
(*user).Name = "..." // тоже работаетЭто сделано для удобства — компилятор сам вставляет *, если это безопасно.
Когда использовать указатели
- Когда нужно изменить значение переменной или структуры внутри функции
- Когда объект большой, и копировать его неэффективно
Задание
Создайте структуру Account с полями:
Owner— имя владельца (строка),Balance— баланс (целое число).
Реализуйте функцию Deposit(), которая принимает указатель на Account и сумму пополнения. Функция должна увеличить баланс на указанную сумму.
Пример
acc := Account{Owner: "Alice", Balance: 100}
Deposit(&acc, 50)
fmt.Println(acc.Balance) // 150Полезное
Ваше упражнение проверяется по этим тестам
package solution
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestDeposit(t *testing.T) {
tests := []struct {
name string
initial int
amount int
expected int
}{
{"Positive deposit", 100, 50, 150},
{"Zero deposit", 100, 0, 100},
{"Negative deposit", 100, -20, 100},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
acc := Account{Owner: "Test", Balance: tt.initial}
Deposit(&acc, tt.amount)
assert.Equal(t, tt.expected, acc.Balance, "Balance mismatch in test case: %s", tt.name)
})
}
}Решение учителя откроется через:
20:00
