Задачи на указатель
1) Что выведет данная программа и почему?
package main
import "fmt"
type Person struct {
Name string
}
func changeName(person *Person) {
person = &Person{
Name: "Alice",
}
}
func main() {
person := &Person{
Name: "Bob",
}
fmt.Println(person.Name)
changeName(person)
fmt.Println(person.Name)
}В данной программе на Go будет выведено:
Bob
BobЭто происходит потому, что в функции changeName, когда мы передаем указатель на структуру Person, мы создаем новый указатель на новый экземпляр Person с именем "Alice", но это изменение не отражается на исходном указателе person в функции main. Поэтому значение имени остается "Bob" как и было изначально.
как исправить эту программу, чтобы изменение имени происходило?
Чтобы изменение имени происходило, нужно изменить функцию changeName так, чтобы она изменяла значение, на которое указывает указатель person, а не создавала новый указатель. Это можно сделать, не присваивая новому указателю, а изменяя значение, на которое указывает person. Вот пример исправленной программы:
package main
import "fmt"
type Person struct {
Name string
}
func changeName(person *Person) {
person.Name = "Alice"
}
func main() {
person := &Person{
Name: "Bob",
}
fmt.Println(person.Name)
changeName(person)
fmt.Println(person.Name)
}В этом примере, когда мы вызываем changeName(person), мы изменяем значение поля Name структуры Person, на которую указывает person. Поэтому, когда мы выводим person.Name после вызова changeName, мы увидим "Alice", а не "Bob".
2) Что выведет на экран данная программа?
package main
import "fmt"
type Count int
func (c Count) Increment(){
c++
}
func main(){
var count Count
count.Increment()
fmt.Print(count)
}В данной программе будет выведено значение 0, а не 1, как могло бы показаться на первый взгляд. Давайте разберем, почему это происходит:
В программе определен новый тип
Countкакint.Далее определен метод
Incrementдля типаCount, который должен увеличивать значение на 1.В функции
mainсоздается переменнаяcountтипаCount.Затем вызывается метод
Incrementдля переменнойcount. Однако, в данном случае методIncrementпринимает значениеCountпо значению, а не по указателю, поэтому изменения не отражаются на исходной переменной.При вызове
count.Increment(), значениеcountкопируется в методIncrement, увеличивается, но изменения не сохраняются в исходной переменнойcount.Поэтому при выводе переменной
countна экран с помощьюfmt.Print(count), будет выведено значение0.
Итак, программа выведет на экран значение 0, так как метод Increment увеличивает копию значения переменной count, но не изменяет саму переменную.
Для исправления данной программы, чтобы она выводила значение 1 вместо 0, необходимо изменить метод Increment так, чтобы он принимал указатель на Count вместо значения Count. Это позволит изменять исходное значение переменной count.
Вот исправленная версия программы:
package main
import "fmt"
type Count int
func (c *Count) Increment() {
*c++
}
func main() {
var count Count
count.Increment()
fmt.Print(count)
}Изменения:
Метод
Incrementтеперь принимает указатель наCountвместо значенияCount:func (c *Count) Increment().Внутри метода
Incrementмы используем разыменование указателя*c++для увеличения значения, на которое указываетc.В
mainфункции мы по-прежнему вызываемcount.Increment(), но теперь это корректно изменяет значение переменнойcount.При выводе
fmt.Print(count)будет напечатано1, так как значениеcountбыло увеличено на 1.
Таким образом, изменив метод Increment так, чтобы он принимал указатель на Count, мы можем корректно изменять значение исходной переменной count и получать ожидаемый результат.
3) Что выведет данная программа и почему?
package main
import "fmt"
func main() {
var s *string
fmt.Println(s == nil)
var i interface{}
fmt.Println(i == nil)
i = s
fmt.Println(i == nil)
}Данная программа на Go выведет следующее:
true
true
falseВот объяснение:
var s *string:Здесь мы объявляем указатель на строку
s, который по умолчанию имеет значениеnil.Поэтому
fmt.Println(s == nil)выводитtrue.
var i interface{}:Здесь мы объявляем переменную
iтипаinterface{}, которая также по умолчанию имеет значениеnil.Поэтому
fmt.Println(i == nil)выводитtrue.
i = s:Здесь мы присваиваем значение
s(которое равноnil) переменнойiтипаinterface{}.Несмотря на то, что
sравенnil,iтеперь содержит указатель на строку, который не равенnil.Поэтому
fmt.Println(i == nil)выводитfalse.
Таким образом, программа выводит:
true- потому чтоsравенnil.true- потому чтоiпо умолчанию равенnil.false- потому что после присваиванияsпеременнойi,iбольше не равенnil.
Ключевой момент здесь в том, что тип interface{} может содержать любое значение, в том числе nil. Поэтому, когда мы присваиваем nil указатель на строку переменной i, i больше не равен nil.
4) Что выведет данный код и почему?
package main
import "fmt"
func main() {
err := do()
if err != nil {
fmt.Println("oops")
} else {
fmt.Println("OK")
}
}
func do() error {
var p *MyError = nil
if false {
p = &MyError{"error"}
}
return p
}
type MyError struct{
msg string
}
func (e MyError) Error() string {
return e.msg
}Этот код демонстрирует интересный случай с нулевыми указателями в Go. Давайте разберем его по частям и объясним, что он выведет и почему.
Объяснение кода
Функция
main:Вызывает функцию
doи проверяет, вернула ли она ошибку.Если ошибка не
nil, выводит "oops", иначе выводит "OK".
Функция
do:Объявляет переменную
pтипа указателя наMyErrorи инициализирует ее значениемnil.Условие
if falseникогда не выполняется, поэтомуpостаетсяnil.Возвращает
p.
Структура
MyError:Определяет структуру с полем
msgтипаstring.Реализует метод
Error, который возвращает значение поляmsg.
Что происходит при выполнении
Переменная
pтипа*MyErrorинициализируется значениемnil.Условие
if falseникогда не выполняется, поэтомуpостаетсяnil.Функция
doвозвращаетnilтипа*MyError.
Важный момент
В Go, если переменная типа error имеет значение nil, это считается отсутствием ошибки. Однако, если переменная типа *MyError имеет значение nil, она все равно может быть приведена к типу error, и это будет корректное значение типа error, но содержащее nil.
Проверка в main
mainВ
main, переменнаяerrбудет иметь значениеnilтипаerror.Условие
if err != nilпроверяет, является лиerrnil.Поскольку
errдействительноnil, условиеif err != nilбудет ложным.
Вывод
Таким образом, программа выведет:
oopsЗаключение
Этот пример показывает, что в Go переменная типа *MyError, имеющая значение nil, может быть приведена к типу error, и это будет корректное значение типа error, но содержащее nil. В результате проверка if err != nil будет ложной, и программа выведет "OK".
Last updated