Задачи на указатель

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, как могло бы показаться на первый взгляд. Давайте разберем, почему это происходит:

  1. В программе определен новый тип Count как int.

  2. Далее определен метод Increment для типа Count, который должен увеличивать значение на 1.

  3. В функции main создается переменная count типа Count.

  4. Затем вызывается метод Increment для переменной count. Однако, в данном случае метод Increment принимает значение Count по значению, а не по указателю, поэтому изменения не отражаются на исходной переменной.

  5. При вызове count.Increment(), значение count копируется в метод Increment, увеличивается, но изменения не сохраняются в исходной переменной count.

  6. Поэтому при выводе переменной 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)
}

Изменения:

  1. Метод Increment теперь принимает указатель на Count вместо значения Count: func (c *Count) Increment().

  2. Внутри метода Increment мы используем разыменование указателя *c++ для увеличения значения, на которое указывает c.

  3. В main функции мы по-прежнему вызываем count.Increment(), но теперь это корректно изменяет значение переменной count.

  4. При выводе 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

Вот объяснение:

  1. var s *string:

    • Здесь мы объявляем указатель на строку s, который по умолчанию имеет значение nil.

    • Поэтому fmt.Println(s == nil) выводит true.

  2. var i interface{}:

    • Здесь мы объявляем переменную i типа interface{}, которая также по умолчанию имеет значение nil.

    • Поэтому fmt.Println(i == nil) выводит true.

  3. i = s:

    • Здесь мы присваиваем значение s (которое равно nil) переменной i типа interface{}.

    • Несмотря на то, что s равен nil, i теперь содержит указатель на строку, который не равен nil.

    • Поэтому fmt.Println(i == nil) выводит false.

Таким образом, программа выводит:

  1. true - потому что s равен nil.

  2. true - потому что i по умолчанию равен nil.

  3. 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. Давайте разберем его по частям и объясним, что он выведет и почему.

Объяснение кода

  1. Функция main:

    • Вызывает функцию do и проверяет, вернула ли она ошибку.

    • Если ошибка не nil, выводит "oops", иначе выводит "OK".

  2. Функция do:

    • Объявляет переменную p типа указателя на MyError и инициализирует ее значением nil.

    • Условие if false никогда не выполняется, поэтому p остается nil.

    • Возвращает p.

  3. Структура 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, переменная err будет иметь значение nil типа error.

  • Условие if err != nil проверяет, является ли err nil.

  • Поскольку err действительно nil, условие if err != nil будет ложным.

Вывод

Таким образом, программа выведет:

oops

Заключение

Этот пример показывает, что в Go переменная типа *MyError, имеющая значение nil, может быть приведена к типу error, и это будет корректное значение типа error, но содержащее nil. В результате проверка if err != nil будет ложной, и программа выведет "OK".

Last updated