日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達者為師

網(wǎng)站首頁 編程語言 正文

GO中什么情況會使用變量逃逸_Golang

作者:阿兵云原生 ? 更新時間: 2023-05-07 編程語言

你知道 GO 中什么情況會變量逃逸嗎?首先我們先來看看什么是變量逃逸

Go 語言將這個以前我們寫 C/C++ 時候需要做的內(nèi)存規(guī)劃和分配,全部整合到了 GO 的編譯器中,GO 中將這個稱為 變量逃逸

GO 通過編譯器分析代碼的特征和代碼的生命周期,決定應(yīng)該使用堆還是棧來進行內(nèi)存分配

C 代碼 和 GO 代碼對比哪個會崩潰?

咱們寫一個簡單的例子,在 C 里面內(nèi)存分配到棧上面還是堆上面是一個很明確的事情

例如

函數(shù)中的變量是分配在棧上面,會隨著該函數(shù)調(diào)用完畢后隨之銷毀掉

程序員自己 malloc 開辟的內(nèi)存是在堆上面,需要程序員自己去釋放

那么問題來了:

如果我們將某一個函數(shù)中的局部變量的地址(全篇以局部變量為例),作為該函數(shù)的返回值,最終在函數(shù)外部去訪問這個局部變量的地址,是否會出錯呢?一起來看看吧

C 程序

test.c

int * get_res(int num){
    int tmp = num + 10;
    return &tmp;
}

int main(){
    int * res = get_res(80);
    printf("%d  -- %p\n" , *res, res);
}

上面寫了一個簡單的 C 代碼,獲取傳入數(shù)據(jù)并 + 10 得到的結(jié)果

# gcc test.c  -o test
test.c: In function ‘get_res':
test.c:7:12: warning: function returns address of local variable [-Wreturn-local-addr]
     return &tmp;
            ^~~~
# ./test
Segmentation fault

這里可以看出編譯程序,報了 warning 了,不過不影響程序的編譯 , 這個 warning 報錯信息是 因為我們返回了臨時變量的地址,C 編譯器檢測到了,給我們拋出了一個 warning

執(zhí)行編譯的程序后,崩潰了 , 熟悉 C 的小伙伴一點都不驚慌,他們不會寫出這種代碼

出現(xiàn) 段錯誤 的原因很明顯,上面有說到,是因為外部訪問了局部變量的地址,外部訪問的時候,此時這個局部變量已經(jīng)被銷毀了,此時外部訪問的這個指針,屬于野指針,因此出現(xiàn)程序崩潰

GO 程序

go 程序的邏輯和上面 C 程序的邏輯一模一樣,那么我們看看是否會出現(xiàn)程序崩潰呢

func getRes(num int) *int {
	tmp := num + 10
	return &tmp
}
func main() {
	res := getRes(80)
	fmt.Printf("%d  -- %p\n", *res, res)
}

執(zhí)行上述代碼,查看效果

# go run main.go
90  -- 0xc420018078

熟悉 go 語言的 小伙伴看到這里心中也毫無波瀾,程序正常執(zhí)行,沒有崩潰,因為他們知道原因,這個現(xiàn)象屬于 變量逃逸

那么我們一起來看看 GO 為什么會這樣做,是如何做的呢?

GO 的逃逸是啥樣子的?

上面有說到 GO 不會像 C/C++ 一樣需要程序員自己去關(guān)心內(nèi)存分配,是期望 GO 程序員更多的關(guān)注邏輯

因此內(nèi)存分配這一塊,GO 編譯器都做的妥妥的,一個變量是分配在棧上面還是堆上面,不是簡單的看一個變量是局部變量就分配到棧上,這個是根據(jù)具體的使用的,有時候它也會被分配到堆上面

當我們發(fā)現(xiàn)本應(yīng)該分配在棧上面的變量,卻分配在堆上面了,說明發(fā)生了逃逸

開始探究和驗證

我們可以嘗試寫一個簡單的 demo ,還是將局部變量的地址返回到外部去,外部來訪問這個局部變量的地址

func getRes(tmp int) *int {

	var t1 int = 1
	var t2 int = 2
	var t3 int = 3

	println(&tmp, &t1, &t2, &t3)

	return &t2
}

func main() {
	res := getRes(80)
	println(*res, res)
}

執(zhí)行上述代碼查看效果

# go run main.go
0xc420045f50 0xc420045f68 0xc420045f60 0xc420045f58
2 0xc420045f60

通過上面的將變量地址打印出來貌似沒有看出上面端倪,地址是也是連續(xù)的

那么我們使用 go 提供的工具來看看這個程序是不是存在逃逸

執(zhí)行 # go tool compile -m main.go 查看效果如下

main.go:11:9: &t2 escapes to heap
main.go:6:6: moved to heap: t2

go tool compile 工具很明顯的調(diào)試出來說明 t2 這個變量已經(jīng)逃逸到 堆上面去了

感興趣的話還可以利用工具瞅一眼匯編,多了解一點也有好處

剛才參數(shù) -m 是直接查看是否逃逸,我們可以加 -S 會打印出具體的會變代碼,查看該變量是否是 new 出來的

# go tool compile -S main.go | grep new
0x0035 00053 (main.go:6)        CALL    runtime.newobject(SB)
rel 54+4 t=8 runtime.newobject+0

對應(yīng)的看看代碼,就是 創(chuàng)建 t2 變量的這一行

對于 go tool compile 工具,我們可以通過 help 命令來查看一下

# go tool compile --help

原文鏈接:https://blog.csdn.net/m0_37322399/article/details/128960879

欄目分類
最近更新