關于 Golang 的參數(shù)傳遞

這里我們著重討論參數(shù)傳遞的方式以及在 Golang 中函數(shù)調用前后(當然包括參數(shù)傳遞)對實參的影響荔茬。先了解一些基本概念废膘。

參數(shù)傳遞

定義

參數(shù)傳遞慕蔚,是在程序運行中,實際參數(shù)就會將參數(shù)值傳遞給相應的形式參數(shù)灌闺,然后在函數(shù)中實現(xiàn)對數(shù)據(jù)處理和返回的過程坏瞄。

  • 實際參數(shù):簡稱實參,在調用函數(shù)/方法時鸠匀,從主調過程傳遞給被調用過程的參數(shù)值。實參可以是變量名缀棍、數(shù)組名、常數(shù)或者表達式父腕。
  • 形式參數(shù):簡稱形參,指出現(xiàn)在函數(shù)/方法形參表中的變量名璧亮。函數(shù)/方法在被調用前沒有為他們分配內存斥难,其作用是說明自變量的類型和形態(tài)以及在過程中的作用。
  • 實參與形參的關系
  1. 形參只能是變量(要指明它的數(shù)據(jù)類型)蘸炸;實參可以是變量、常量或者表達式穷当。
  2. 實參與形參的個數(shù)淹禾、位置以及它們對應的數(shù)據(jù)類型應當一致馁菜。
  3. 調用函數(shù)時若出現(xiàn)實參時數(shù)組名铃岔,則傳遞給形參的時數(shù)組的首地址。
  4. 實參傳遞給形參是單向傳遞智嚷。形參變量在未出現(xiàn)函數(shù)調用時并不占用內存,只在調用時才占用盏道。調用結束后將釋放內存载碌。

方法

按值傳遞參數(shù)

按值傳遞參數(shù)時,是將實參變量的值復制到一個臨時存儲單元中嫁艇。如果在調用過程中改變了形參的值,不會影響實參變量本身论皆,即實參變量保持調用前的值不變。

按地址傳遞參數(shù)

按地址傳遞參數(shù)時纯丸,把實參變量的地址傳送給被調用過程静袖,實參和形參共用同一內存地址。在被調用過程中队橙,形參的值一旦改變。相應實參的值也跟著改變捐康。如果實參是一個常數(shù)或者表達式(不含變量的表達式,也可當作常數(shù))贮匕,則按傳值方式處理花枫。

按數(shù)組傳遞參數(shù)

按照按地址傳遞的方式傳遞數(shù)組掏膏。當數(shù)組作為實參傳遞給函數(shù)/方法敦锌,系統(tǒng)將實參數(shù)組的起始地址傳給過程使形參數(shù)組也具有與實參數(shù)組相同的起始地址。

Golang 中的參數(shù)傳遞

值傳遞

事實證明 Golang 的參數(shù)傳遞(目前我接觸的常用的類型如: string 乙墙、 int 、 bool 听想、array 、 slice 衔峰、 map 、 chan )都是值傳遞朽色。

func main() {
    b := false
    fmt.Println("b's address is:", &b)
    bo(b)
    fmt.Println(b)
}

func bo(b bool) {
    fmt.Println("this address is different from the original address:", &b)
    b = true
}

// Output:
// b's address is: 0xc0420361ac
// this address is different from the original address: 0xc0420361fa
// false

從上面代碼可以看出在函數(shù)中修改值不會影響實參的原始值组题。其余的類型讀者自行嘗試輸出查看結果。若要在函數(shù)中改變實參的值崔列,則使用指針傳遞

var i int = 5

func main() {
    modify(&i)
    fmt.Println(i)
}

func modify(i *int) {
    *i = 6
}

// Output:
// 6

關于 slice 的參數(shù)傳遞

數(shù)組的參數(shù)傳遞

使用數(shù)組元素(array[x])或者數(shù)組(array)作為函數(shù)參數(shù)時,其使用方法和普通變量相同盈咳。即是值傳遞边翼。

func modifyElem(a int) {
    a += 100
}

func modifyArray(a [5]int) {
    a = [5]int{5,5,5,5,5}
}

func main() {
    var s = [5]int{1, 2, 3, 4, 5}
    modifyElem(s[0])
    fmt.Println(s[0])
    modifyArray(s)
    fmt.Println(s)
}

// Output:
// 1
// [1 2 3 4 5]

slice 的參數(shù)傳遞

slice 作為實參傳入函數(shù)也是進行的值傳遞。但是slice引用array组底。

// modify s
func modify(s []int) {
    fmt.Printf("%p \n", &s)
    s = []int{1,1,1,1}
    fmt.Println(s)
    fmt.Printf("%p \n", &s)
}

func main() {
    a := [5]int{1, 2, 3, 4, 5}
    s := a[:]
    fmt.Printf("%p \n", &s)
    modify(s)
    fmt.Println(s[3])
}

// Output:
// 0xc042002680 
// 0xc0420026c0 
// [1 1 1 1]
// 0xc0420026c0 
// 4

可以看到,實參傳遞之前的地址和在函數(shù)里面的地址是不同的江滨,而且在函數(shù)里面修改實參的值也不會影響實參的實際值厌均。當然,在函數(shù)里面對 slice 進行重新賦值不會改變它的地址(因為這里輸出了兩個相同的地址)。但是下面一段代碼可能有點讓人迷惑:

// modify s[0] value
func modify(s1 []int) {
    s1[0] += 100
}

func main() {
    a := [5]int{1, 2, 3, 4, 5}
    s := a[:]
    modify(s)
    fmt.Println(s[0])
}

// Output:
// 101

在 StackOverFlow 上有人做出了解釋擒悬, Are golang slices pass by value? 摘要如下:

Everything in Go is passed by value. Slices too. But a slice value is a header, describing a contiguous section of a backing array, and a slice value only contains a pointer to the array where the elements are actually stored. The slice value does not include its elements (unlike arrays).

So when you pass a slice to a function, a copy will be made from this header, including the pointer, which will point to the same backing array. Modifying the elements of the slice implies modifying the elements of the backing array, and so all slices which share the same backing array will "observe" the change.

簡單地說: slice 作為參數(shù)傳遞給函數(shù)其實是傳遞 slice 的值惹挟,這個值被稱作一個 header 缝驳,它只包含了一個指向底層數(shù)組的指針。當向函數(shù)傳遞一個 slice 用狱,將復制一個 header 的副本,這個副本包含一個指向同一個底層數(shù)組的指針摇展。修改 slice 的元素間接地修改底層數(shù)組的元素,也就是所有指向同一個底層數(shù)組的 slice 會響應這個變化咏连,主函數(shù)的 slice 也就一同修改了 s[0] 的值鲁森。

關于這個問題,其實我們只要在每個操作上面輸出它的地址歌溉,只要地址不變,就說明修改會對 main 函數(shù)里面的 slice 產(chǎn)生影響痛垛,對上面的代碼進一步修改:

// modify s[0] value
func modify2(s []int) {
    fmt.Printf("%p \n", &s)
    fmt.Printf("%p \n", &s[0])
    s[0] += 100
    fmt.Printf("%p \n", &s)
    fmt.Printf("%p \n", &s[0])
}

func main() {
    a := [5]int{1, 2, 3, 4, 5}
    s := a[:]

    fmt.Printf("%p \n", &s)
    fmt.Printf("%p \n", &s[0])
    modify2(s)
    fmt.Println(s[0])
}

// Output:
// 0xc04203c400 
// 0xc042039f50 
// 0xc04203c440 
// 0xc042039f50 
// 0xc04203c440 
// 0xc042039f50 
// 101

實參傳遞給函數(shù)的只是一個 slice 的副本匙头,它們不是指向同一個內存地址的。在 main 函數(shù)和 modify2 函數(shù)里面我們打印了 s[0] 的內存地址蹂析,發(fā)現(xiàn)它們的內存地址是相同的,所以當我們在 modify2 函數(shù)里面修改 s[0] 會影響 s[0] 的原始值识窿。

在sof搜了一下,發(fā)現(xiàn)這個回答比較易懂缩宜,做個記錄。

Github锻煌, Go 愛好者,不定時更新宋梧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市释涛,隨后出現(xiàn)的幾起案子倦沧,更是在濱河造成了極大的恐慌,老刑警劉巖展融,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扑浸,居然都是意外死亡燕偶,警方通過查閱死者的電腦和手機喝噪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門仙逻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涧尿,“玉大人系奉,你說我怎么就攤上這事姑廉。” “怎么了萌踱?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵号阿,是天一觀的道長。 經(jīng)常有香客問我扔涧,道長届谈,這世上最難降的妖魔是什么弯汰? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮咏闪,結果婚禮上,老公的妹妹穿的比我還像新娘纵装。我一直安慰自己溪胶,他們只是感情好搂擦,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扳还,像睡著了一般。 火紅的嫁衣襯著肌膚如雪氨距。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天滔驶,我揣著相機與錄音仍劈,去河邊找鬼。 笑死奄容,一個胖子當著我的面吹牛甩十,可吹牛的內容都是我干的濒募。 我是一名探鬼主播赊颠,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼竣蹦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了痘括?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤抄淑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后肆资,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡唉韭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年属愤,在試婚紗的時候發(fā)現(xiàn)自己被綠了酸役。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡贱呐,死狀恐怖入桂,靈堂內的尸體忽然破棺而出奄薇,到底是詐尸還是另有隱情抗愁,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布沫屡,位于F島的核電站逢捺,受9級特大地震影響,放射性物質發(fā)生泄漏倘潜。R本人自食惡果不足惜志于,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望伺绽。 院中可真熱鬧嗜湃,春花似錦澜掩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至乔妈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間勃刨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工朵你, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留揣非,地道東北人躲因。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓大脉,卻偏偏與公主長得像搞监,于是被迫代替她去往敵國和親镰矿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內容