2016-11-23 35 views
2

我還在學習中,需要一隻手來清理我的頭。指針新手 - 更正我

以下程序輸出Power值爲1每個Println。我期待1作爲第一個輸出和2作爲第二個輸出。 我的假設是Changefuncnew address覆蓋address ofs,並且此更改將反映回調用方(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,則可以更改該函數外部的指針地址。

當然,就像Andy Schweig所說的那樣,儘管如此,你可能不會那麼做,並且可能會根據需要使用函數的普通指針版本更改單個字段。

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轉到實際上這樣做(*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

是的。那與我忘記了一會兒的時間,現在回想起我在大學裏學到的東西有關。一段時間以來,我們一直在用所有新的gen語言去指針。不理我。 – Nair

2

將參數的值更改爲函數內部的函數從不影響調用者傳遞的參數。所有參數都按值傳遞。如果要更改s(調用方中的pod)指向的對象內的Power的值,請使用s.Power = 2。如果實際上想要將主叫方中的指針變量設置爲不同的Pod對象,則需要將參數Change聲明爲s **pos,將函數中的分配更改爲*s = &Pod{2},並調用Change(&pod)。雖然這可能不是你想要做的。

+0

感謝您的評論。 – Nair