網(wǎng)站建設(shè)的招聘要求張家口網(wǎng)站seo
程序中編寫的函數(shù)在編譯階段會(huì)被編譯成一段段的指令存放在可執(zhí)行文件中,在程序運(yùn)行階段這些內(nèi)存會(huì)加載到虛擬地址空間的代碼段。
當(dāng)函數(shù)A調(diào)用了函數(shù)B的時(shí)候,對應(yīng)的會(huì)生成一條call指令,程序在運(yùn)行到call指令時(shí)就會(huì)跳轉(zhuǎn)到對應(yīng)的B函數(shù)的代碼段的方法入口。每個(gè)函數(shù)最后還有一條ret指令,用于在函數(shù)執(zhí)行結(jié)束時(shí)跳回到調(diào)用處。
函數(shù)的??臻g一般從?;鵥p開始到棧頂sp結(jié)束。從bp到sp依次存儲(chǔ)了
1.調(diào)用者?;刂?caller bp
2.局部變量
3.返回值
4.參數(shù)
函數(shù)的運(yùn)行需要一些關(guān)鍵信息,包括局部變量、參數(shù)、返回值等等。這些信息存放在內(nèi)存棧中。棧空間的數(shù)據(jù)后進(jìn)先出,比如上面A調(diào)用B,會(huì)先加載A需要的信息到棧內(nèi),再調(diào)用到B時(shí)加載B需要的信息到棧內(nèi),B執(zhí)行完后將B用到的信息彈出棧。
call指令會(huì)將下一條指令的地址入棧,及A棧幀后面接了一條返回地址信息,然后跳轉(zhuǎn)到被調(diào)用函數(shù)入口出執(zhí)行,所以??臻g內(nèi)會(huì)入棧B函數(shù)的棧幀
現(xiàn)在棧空間內(nèi)依次為 :A棧幀->返回地址->B棧幀
程序運(yùn)行時(shí)每個(gè)函數(shù)的棧布局都遵守統(tǒng)一的約定,所以被調(diào)用者可以通過棧指針+偏移量定位到特定的參數(shù)和返回值。
return關(guān)鍵字并不是原子性的,先是將返回值賦值,然后執(zhí)行defer函數(shù),在返回返回值
例1:
func func1(a int) int {defer func() {a++}()a++return a
}
func main() {a := func1(0)fmt.Println(a) // 輸出1
}
- a++,參數(shù)a=1
- 執(zhí)行return,將1賦值給返回值空間,此時(shí)返回值為1
- 執(zhí)行defer,將參數(shù)a++,此時(shí)a=2,但返回值空間依然為1
- 函數(shù)調(diào)用結(jié)束,返回1
再看一段代碼
func func1(a int) (b int) {defer func() {a++b++}()a++return a
}
func main() {a := func1(0)fmt.Println(a) // 輸出2
}
- a++,參數(shù)a=1
- 執(zhí)行return,將1賦值給返回值空間,此時(shí)返回值為1
- 執(zhí)行defer,將參數(shù)a++,此時(shí)a=2,然后將返回值b++,此時(shí)b=2
- 調(diào)用結(jié)束,返回2
理解了函數(shù)調(diào)用時(shí)數(shù)據(jù)的分配就可以理解上面的問題。
另一個(gè)關(guān)鍵點(diǎn)是指針參數(shù)問題
func func1(a *int) {defer func() {*a++}()*a++return
}
func main() {a := 0func1(&a)fmt.Println(a) // 輸出2
}
golang中方法都是值傳遞,但是傳遞的值是指針類型,里面存放的是數(shù)據(jù)的地址。
下面看一下引用類型的例子
func func1(a []int) {a[0] = 1return
}
func main() {a := []int{0}func1(a)fmt.Println(a) // 輸出[1]
}
這段代碼中 調(diào)用func1時(shí)傳遞的是slice類型的參數(shù),slice類型是引用的底層數(shù)據(jù),所以func1改變數(shù)據(jù)底層數(shù)據(jù)時(shí),main中的局部變量a也受到了改變。
func func1(a []int) {a = append(a, 1, 2, 3)return
}
func main() {a := []int{0}func1(a)fmt.Println(a) // 輸出[0]
}
這段代碼因?yàn)閒unc1對a執(zhí)行了append,觸發(fā)了slice的擴(kuò)容,底層開辟了一個(gè)新的數(shù)組并重新引用了新的數(shù)組,所以原數(shù)組沒有受到影響。