2016-11-23 3 views
2

Я все еще учусь и требую руки, чтобы очистить голову.Pointer newbie - исправить меня

Следующий выход программы Power значение как 1 в каждом Println. Я ожидал 1 в качестве первого выхода и 2 в качестве второго выхода. Мое предположение было Changefunc перезаписать address ofs с new address, и это изменение отразится обратно на вызывающего абонента (main func). В этом случае исходный адрес будет указывать на вновь созданный адрес, когда он вызывает второй Println. Мое предположение неверно, но я не могу понять, почему.

package main 

import (
    "fmt" 

) 
type Pod struct{ 
    Power int 
} 
func main() { 
    pod := &Pod{1} 
    fmt.Println(pod.Power) 
    Change(pod) 
    fmt.Println(pod.Power) 
} 
func Change(s *Pod) { 
    s = &Pod{2} 
} 

Code

Для дальнейшего изучения того, что происходит под прикрытием, я пытался выводить адреса на этот раз, и это выглядит, как показано ниже;

import (
    "fmt" 
) 

type Pod struct{ 
    Power int 
} 
func main() { 
    pod := &Pod{ 1} 
    fmt.Println(&pod) //0xc04202c020 
    Change(pod) 
    fmt.Println(&pod) //0xc04202c020 
} 
func Change(s *Pod) { 
    fmt.Println(&s) //0xc04202c030 (I was expecting 0xc04202c020 here) 
    s = &Pod{ 2} 
    fmt.Println(&s) //0xc04202c030 
} 

ответ

4

Это потому что, когда вы передаете аргументы функции и т. Д., Они всегда передаются по значению.

Другими словами, даже если вы принимаете указатель в параметре функции, адрес структуры будет скопирован.

Затем, когда вы назначаете s, чтобы попытаться изменить адрес, он изменяет только локальную копию, а не ту, которая находится вне функции.

Для изменения адреса из функции вам нужно будет передать указатель указателя, а затем присвоить разыменованный s, например:

package main 

import (
    "fmt" 
) 

type Pod struct { 
    Power int 
} 

func main() { 
    pod := &Pod{1} 
    fmt.Println(pod.Power) 
    Change(&pod) 
    fmt.Println(pod.Power) 
} 

func Change(s **Pod) { 
    *s = &Pod{2} 
} 

В этом случае копия адреса по-прежнему передается в функцию, но поскольку это указатель на указатель, это означает, что когда вы разыскиваете s как *s, вы получите адрес структуры вне функции. Это означает, что если вы назначаете *s, вы можете изменить адрес указателя вне функции.

Конечно, как говорит Энди Швайг, вы, вероятно, не хотели бы этого делать, и, вероятно, просто изменили бы отдельные поля по мере необходимости с обычной версией функции указателя.

package main 

import (
    "fmt" 
) 

type Pod struct { 
    Power int 
} 

func main() { 
    pod := &Pod{1} 
    fmt.Println(pod.Power) 
    Change(pod) 
    fmt.Println(pod.Power) 
} 

func Change(s *Pod) { 
    s.Power = 2 
} 

Это работает, потому что, когда вы набираете s.Power = 2 Go будет на самом деле сделать что-то вроде (*s).Power = 2 для вас. Таким образом, это автоматически разыгрывает s для вас, что дает вам фактическую структуру Pod.

Вы не можете сделать *s = &Pod{2} в этом обычном примере указателя, потому что в этом случае *s будет фактически равен тип Pod, не *Pod.

Из-за этого, если вы хотите использовать синтаксис &Pod{2} для назначения адреса, вам нужно передать указатель на указатель. В случае s **Pod разыменование *s будет указывать на адрес Pod вместо фактического Pod, поэтому *s будет иметь тип *Pod, который позволяет назначить &Pod{2}.

Сказав все этого, **Pod только требуется, если вы хотите назначить адрес с синтаксисом &Pod{2}.

Если вам не нужен синтаксис &Pod{2}, вы можете просто разыменовать s и назначить нормальный Pod{2}.

package main 

import (
    "fmt" 
) 

type Pod struct { 
    Power int 
} 

func main() { 
    pod := &Pod{1} 
    fmt.Println(pod.Power) 
    Change(pod) 
    fmt.Println(pod.Power) 
} 

func Change(s *Pod) { 
    *s = Pod{2} 
} 

Это также работает, потому что теперь s является копией адреса, и когда вы разыменования вы получите значения вне функции, типа Pod, а не *Pod.

Это означает, что вы можете просто назначить ему, удалив &.

В принципе, если вы используете &, это значит, что вы хотите присвоить адрес, а не действительное значение.

Я надеюсь, что это объяснение не слишком смущает. Я объяснил, используя **Pod, потому что я думал, что вы хотите использовать синтаксис &Pod{2}, а не Pod{2}, но если это не так, мои примеры s.Power = 2 или *s = Pod{2} могут иметь больше смысла.

+0

Спасибо и хорошо объяснил. – Nair

+0

@Nair Я видел ваш предыдущий комментарий, который ушел сейчас, но из-за этого я только что отредактировал ответ, чтобы предоставить дополнительную информацию. Надеюсь, это вам поможет :) –

+0

Да. Это было связано с указателем, который я забыл на мгновение, и теперь вспоминал то, что я узнал в колледже. Мы уже давно не указали указатель на все языки нового поколения. Игнорируй меня. – Nair

2

Изменение значения параметра функции внутри функции никогда не влияет на параметр, переданный вызывающим абонентом. Все параметры передаются по значению. Если вы хотите изменить значение Power внутри объекта, на который указывает s (pod у вызывающего абонента), используйте s.Power = 2. Если вы действительно хотите установить переменную указателя в вызывающем объекте на другой объект Pod, вам необходимо объявить параметр Change как s **pos, изменить назначение в функции на *s = &Pod{2} и позвонить по телефону Change(&pod). Это, вероятно, не то, что вы хотите сделать.

+0

Спасибо за ваш комментарий. – Nair