【Go參數(shù)傳遞:值類型豁跑、引用類型和指針類型】

修改參數(shù)

func main() {
    p := person{name : "zhangsan"廉涕, age : 18}
    modifyPerson(p)
    
    fmt.Println("name:", p.name, "age:", p.age)
}

func modifyPerson(p person) {
    p.name = "lisi"
    p.age = 19
}

type person struct {
    name string
    age int 
}

// 輸出結(jié)果
name:zhangsan, age:18    // 發(fā)現(xiàn)結(jié)果沒有被改變艇拍,我們改成指針類型試試呢狐蜕?


modifyPerson(&p)
func modifyPerson(p *person){
    p.name = "lisi"
    p.age = 19
}

// 輸出結(jié)果
name: lisi, age: 19   // 接收參數(shù)修改為指針參數(shù),就可以滿足需求了卸夕。

值類型

上述示例中层释,定義的普通變量p是person類型的。在Go語言中快集,**person是一個值類型贡羔,而&p獲取的指針是*person類型的,即指針類型**个初。那么為什么值類型在參數(shù)傳遞中無法修改呢乖寒? 要從內(nèi)存講起。

變量的值是存儲在內(nèi)存中的院溺,而內(nèi)存都有一個編號楣嘁,稱為內(nèi)存地址。所以要想修改內(nèi)存中的數(shù)據(jù),就要找到這個內(nèi)存地址逐虚。我們來對比值類型變量在函數(shù)內(nèi)外的內(nèi)存地址聋溜,如下
func main() {
    p := person{name : "zhangsan", age : 18}
    fmt.Println("main函數(shù)叭爱,p的內(nèi)存地址:", &p)
    modifyPerson(p)
    
    fmt.Println("name:", p.name, "age:", p.age)
}

func modifyPerson(p person) {
    fmt.Println("modifyPerson 函數(shù)p的內(nèi)存地址:", &p)
    p.name = "lisi"
    p.age = 19
}

// 輸出結(jié)果
main函數(shù)撮躁,p的內(nèi)存地址: 0x0000a3020
modifyPerson 函數(shù)p的內(nèi)存地址:0x0000a3040
name:zhangsan, age:18

// 我們發(fā)現(xiàn)內(nèi)存地址不一樣,意味著买雾,在modifyPerson函數(shù)中修改的參數(shù)p和main函數(shù)中的變量p不是同一個馒胆,這就是為什么我們在modifyPerson函數(shù)中修改了參數(shù)p,但是在main函數(shù)中打印結(jié)果中沒有修改的原因
導(dǎo)致這種結(jié)果的原因是**Go語言中函數(shù)傳參都是值傳遞**凝果。值傳遞值得是傳遞原來數(shù)據(jù)的一份拷貝,而不是原來的數(shù)據(jù)本身睦尽。
image-20211217101053089
調(diào)用modifyPerson函數(shù)傳遞變量p的時候器净,Go語言會拷貝一個p放在新的內(nèi)存中,這樣新的p的內(nèi)存地址就和原來的不一樣了当凡,但是里面的值是一樣的山害。即**副本的意思**,變量中的數(shù)據(jù)一樣沿量,但是內(nèi)存地址不一樣浪慌。

除了struct外,**浮點(diǎn)型朴则、整型权纤、字符串、布爾乌妒、數(shù)組汹想,這些都是值類型**。

指針類型

指針類型的變量保存的值就是數(shù)據(jù)對應(yīng)的內(nèi)存地址撤蚊,所以在函數(shù)參數(shù)傳遞是傳值的原則下古掏,拷貝的值也是內(nèi)存地址。
func main() {
    p := person{name : "zhangsan"侦啸, age : 18}
    fmt.Println("main函數(shù)槽唾,p的內(nèi)存地址:", &p)
    modifyPerson(&p)
    
    fmt.Println("name:", p.name, "age:", p.age)
}

func modifyPerson(p *person) {
    fmt.Println("modifyPerson 函數(shù)p的內(nèi)存地址:", &p)
    p.name = "lisi"
    p.age = 19
}

// 輸出結(jié)果
main函數(shù),p的內(nèi)存地址: 0x0000a3020
modifyPerson 函數(shù)p的內(nèi)存地址:0x0000a3020
name:lisi, age:19
**指針類型的參數(shù)是永遠(yuǎn)可以修改原數(shù)據(jù)的光涂,因?yàn)樵趨?shù)傳遞時庞萍,傳遞的是內(nèi)存地址。**

提示: 值傳遞的是指針忘闻,即內(nèi)存地址挂绰。通過內(nèi)存地址可以找到元數(shù)據(jù)的那塊內(nèi)存,所以修改它也就等于修改了原數(shù)據(jù)。

引用類型

引用類型葵蒂,包括map交播、slice和chan。

**map**
func main() {
    m := make(map[string]int)
    m["奔跑的蝸牛"] = 18
    fmt.Println("age:" : m["奔跑的蝸牛"])
    modifyMap(m)
    fmt.Println("age:" : m["奔跑的蝸牛"])
}

func modifyMap(m map[string]int) {
    p["奔跑的蝸牛"] = 19
}

// 輸出結(jié)果
age:18
age:19

// 我們發(fā)現(xiàn)修改成功了践付。為什么沒有使用指針秦士,只是使用了map類型的參數(shù),按照Go語言值傳遞原則永高,modifyMap函數(shù)中map是一個副本隧土,為什么修改成功了呢?
一切原因要從 **make 這個Go語言內(nèi)建的函數(shù)說起**命爬。在Go語言中曹傀,任何創(chuàng)建map的代碼(不管是字面量還是make函數(shù))最終調(diào)用的都是 **runtime.makemap函數(shù)**。

提示: 用字面量或者make函數(shù)的方式創(chuàng)建map饲宛,并轉(zhuǎn)換成 makemap 函數(shù)的調(diào)用皆愉,這個轉(zhuǎn)換是Go語言編譯器自動幫我們做的。

func makmap(t *maptype, hint int, h*hmap) *hamp{
    
}
從源碼可以看出艇抠,Go語言的map類型本質(zhì)上就是 *hmap幕庐,所以根據(jù)替換原則,`modifyMap(a map) 函數(shù)其實(shí)就是modifyMap(a *hmap)`家淤。 這就和上面的指針類型的參數(shù)調(diào)用一樣了异剥,也就是通過map類型的參數(shù)可以修改原始數(shù)據(jù)的原因,因?yàn)楸举|(zhì)上就是指針絮重。

為了驗(yàn)證創(chuàng)建的map是一個指針冤寿,修改上述示例,
func main() {
    m := make(map[string]int)
    m["奔跑的蝸牛"] = 18
    fmt.Println("age:" : m["奔跑的蝸牛"])
    fmt.Println("main函數(shù)內(nèi)存地址:", m)
    modifyMap(m)
    fmt.Println("age:" : m["奔跑的蝸牛"])
}

func modifyMap(m map[string]int) {
    p["奔跑的蝸牛"] = 19
    fmt.Println("modifyMap函數(shù)內(nèi)存地址:", m)
}

// 輸出結(jié)果
age:18
main函數(shù)內(nèi)存地址:0x000060170
age:19
modifyMap函數(shù)內(nèi)存地址:0x000060170

// 從輸出結(jié)果看青伤,內(nèi)存地址一模一樣疚沐,所以可以修改原始數(shù)據(jù)。而且在打印指針的時候潮模,直接使用的是變量m和p亮蛔,并沒有用取地址符&,因?yàn)樗麄儽臼【褪侵羔樓嫦幔詻]必要在使用&取地址了
Go語言通過make函數(shù)或字面量的包裝為我們省去了指針的操作究流,讓我們可以更容易的使用map。其實(shí)就是**語法糖**动遭,這是編程界的老傳統(tǒng)了芬探。

注意: 這里的map可以理解為引用類型, 但是它本質(zhì)是指針厘惦,只是可以叫做引用類型而已偷仿。在參數(shù)傳遞時哩簿,它還是值傳遞,并不是其他編程語言中所謂的引用傳遞酝静。

chan

channel也可以理解為引用類型节榜, 而它本質(zhì)也是一個指針。
func makechan(t *chantype, size int64) *hchan{}

// 從源碼可以看到别智,所創(chuàng)建的chan其實(shí)是個*hchan宗苍,所以它在參數(shù)傳遞中也和map一樣
**嚴(yán)格的說,Go語言沒有引用類型**薄榛,但是我們可以把map讳窟、chan稱為引用類型,這樣便于理解敞恋。除了map丽啡、chan外,Go語言中的函數(shù)硬猫、接口补箍、slice切片都可以稱為引用類型。

類型零值

在Go語言中浦徊,定義變量要么通過聲明、要么通過make和new函數(shù)天梧,不一樣的是make和new函數(shù)屬于顯示聲明并初始化盔性。如果我們聲明的變量沒有顯示聲明初始化,那么該變量的默認(rèn)值就是對應(yīng)類型的零值呢岗。
類型 零值
數(shù)值類型(int冕香、float) 0
bool false
string ""(空字符串)
struct 內(nèi)部字段零值
slice nil
map nil
指針 nil
函數(shù) nil
chan nil
interface nil

總結(jié):在Go語言中,函數(shù)的參數(shù)傳遞只有值傳遞后豫,而且傳遞的實(shí)參都是原始數(shù)據(jù)的一份拷貝悉尾。如果拷貝的內(nèi)容是值類型的,那么在函數(shù)中無法修改原始數(shù)據(jù)挫酿,如果拷貝的內(nèi)容是指針(或者可以理解為引用類型)构眯,那么可以在函數(shù)中修改原始數(shù)據(jù)。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末早龟,一起剝皮案震驚了整個濱河市惫霸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌葱弟,老刑警劉巖壹店,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芝加,居然都是意外死亡硅卢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來将塑,“玉大人脉顿,你說我怎么就攤上這事√” “怎么了弊予?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長开财。 經(jīng)常有香客問我汉柒,道長,這世上最難降的妖魔是什么责鳍? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任碾褂,我火速辦了婚禮,結(jié)果婚禮上历葛,老公的妹妹穿的比我還像新娘正塌。我一直安慰自己,他們只是感情好恤溶,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布乓诽。 她就那樣靜靜地躺著,像睡著了一般咒程。 火紅的嫁衣襯著肌膚如雪鸠天。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天帐姻,我揣著相機(jī)與錄音稠集,去河邊找鬼。 笑死饥瓷,一個胖子當(dāng)著我的面吹牛剥纷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播呢铆,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼晦鞋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了棺克?” 一聲冷哼從身側(cè)響起鳖宾,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逆航,沒想到半個月后鼎文,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡因俐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年拇惋,在試婚紗的時候發(fā)現(xiàn)自己被綠了周偎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡撑帖,死狀恐怖蓉坎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情胡嘿,我是刑警寧澤蛉艾,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站衷敌,受9級特大地震影響勿侯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缴罗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一助琐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧面氓,春花似錦兵钮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間于颖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工酿箭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留复亏,地道東北人趾娃。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像缔御,于是被迫代替她去往敵國和親抬闷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容