Почему типы указателей ведут себя иначе, чем типы структур? - PullRequest
0 голосов
/ 05 декабря 2018

У меня есть этот фрагмент кода Go, где я пытаюсь изменить значения обычного int и int в структуре, используя две функции f и f2 соответственно.Я не понимаю, почему мне нужно сделать *i, чтобы изменить значение типа int, но мне не нужно это делать, когда я изменяю значение X в структуре.

type Point struct {
    X int
}

func t(i *int) {
    *i = 20
}

func t2(p *Point) {
    p.X = 200
}

func main() {
    g := 30
    t(&g)
    fmt.Println(g)

    p := Point{3}
    t2(&p)
    fmt.Println(p)
}

Ответы [ 2 ]

0 голосов
/ 05 декабря 2018

Самый простой способ думать об обеих функциях состоит в том, что в функции t2 вы изменяете поле структуры, используя указатель на базовую структуру.В функции t вы изменяете весь базовый объект (int).

На самом деле тот факт, что вы можете написать p.X, на самом деле просто приятная вещь.В таких языках, как C, вы можете использовать p.X, только если вы работаете с переменной без указателя.Для указателей вы должны были либо использовать p->X, обозначая, что вы обращались к полю с помощью косвенного обращения, либо действительно разыменовывать указатель ((*p).X).

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

Обе функции, однако, не эквивалентны.Point - это структура с одним или несколькими полями.Другой тип - *int, int - это одно скалярное значение.Чтобы сделать t2 эквивалентным (и переназначить весь базовый объект), вам придется изменить код на идентичный тому, что вы должны сделать в случае *int:

func t2(p *Point) {
    *p = Point{
         X: 200,
         Y: p.Y,
     }
}

Согласно комментарию ниже: версия TL; DR заключается в том, что вам не нужно явно разыменовывать указатель на тип структуры, если вы обращаетесь к одному из его полей.Вы должны были сделать это в C / C ++, но компилятор go позаботится об этом за вас.Получается, что вы используете переменную типа указателя и компилируете p.X так же, как компилятор C компилирует p->X.Следовательно, вам не нужно явно разыменовывать p.

Вам все равно придется писать *p.X, если вы объявили Point как:

type Point struct {
    X *int
}

Поскольку выражение p.X соответствует операнду типа *int, который должен обрабатываться соответствующим образом.

0 голосов
/ 05 декабря 2018

Поскольку i является указателем типа *int, и вы хотите изменить указанный объект, вам нужно написать *i.

Тот же синтаксис действителен и для структур, напримерВы можете написать (*p).X, но это частая операция, поэтому спецификация позволяет использовать p.X, что будет означать (*p).X, нет никакой двусмысленности и является удобным сочетанием клавиш.

Это в Spec: Селекторы:

В качестве исключения, если тип x является определенным типом указателя и (*x).f является допустимым выражением селектора, обозначающимполе (но не метод), * ​​1020 * является сокращением для (*x).f.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...