> For the complete documentation index, see [llms.txt](https://yuliyas-organization-3.gitbook.io/prokhodim-sobesedovanie-na-golang-razrabotchika/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yuliyas-organization-3.gitbook.io/prokhodim-sobesedovanie-na-golang-razrabotchika/golang-zadachi/zadachi-na-ukazatel.md).

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

### 1) Что выведет данная программа и почему?

```go
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`. Вот пример исправленной программы:

```go
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) Что выведет на экран данная программа?

```go
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`.

Вот исправленная версия программы:

```go
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) Что выведет данная программа и почему?

```go
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) Что выведет данный код и почему?

```go
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".
