在討論之前,先看如下代碼:
1 type treeNode struct { 2 value int 3 left, right *treeNode 4 } 5 6 func createNode(value int) *treeNode { 7 return &treeNode{value:value} 8 } 9 10 func main() { 11 root := createNode(10) 12 fmt.Println(root) 13 }
上面這段代碼createNode函數返回了一個局部變量的地址給main函數中的root,但是fmt.Println正常打印出來了新建的node的內容。這要是在C++中這么寫,是個很典型的錯誤:返回局部變量的地址,該地址的內容在函數退出后會被自動釋放,因為是在棧上的。
那么go語言的局部變量到底是在棧上還是堆上呢?go語言編譯器會做逃逸分析(escape analysis),分析局部變量的作用域是否逃出函數的作用域,要是沒有,那么就放在棧上;要是變量的作用域超出了函數的作用域,那么就自動放在堆上。所以不用擔心會不會memory leak,因為go語言有強大的垃圾回收機制。這樣可以釋放程序員的內存使用限制,讓程序員關注程序邏輯本身。
對於new出來的局部變量,也不是一定就放在堆上,而是根據其是否超出了函數作用域來判斷是否放在堆上還是棧上。這點和C語言很不一樣。