1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 12:32:21 +00:00

ch1-basic: add some description about stack

This commit is contained in:
leobuzhi 2018-09-08 23:54:31 +08:00
parent 41b984c32f
commit 32f9de8779

View File

@ -143,9 +143,9 @@ func twice(x IntSliceHeader) {
因为切片中的底层数组部分是通过隐式指针传递(指针本身依然是传值的,但是指针指向的却是同一份的数据)所以被调用函数是可以通过指针修改掉调用参数切片中的数据。除了数据之外切片结构还包含了切片长度和切片容量信息这2个信息也是传值的。如果被调用函数中修改了`Len``Cap`信息的话,就无法反映到调用参数的切片中,这时候我们一般会通过返回修改后的切片来更新之前的切片。这也是为何内置的`append`必须要返回一个切片的原因。 因为切片中的底层数组部分是通过隐式指针传递(指针本身依然是传值的,但是指针指向的却是同一份的数据)所以被调用函数是可以通过指针修改掉调用参数切片中的数据。除了数据之外切片结构还包含了切片长度和切片容量信息这2个信息也是传值的。如果被调用函数中修改了`Len``Cap`信息的话,就无法反映到调用参数的切片中,这时候我们一般会通过返回修改后的切片来更新之前的切片。这也是为何内置的`append`必须要返回一个切片的原因。
Go语言中函数还可以直接或间接地调用自己也就是支持递归调用。Go语言函数的递归调用深度逻辑上没有限制函数调用的栈是不会出现溢出错误的因为Go语言运行时会根据需要动态地调整函数栈的大小。每个goroutine刚启动时只会分配很小的栈4或8KB具体依赖实现根据需要动态调整栈的大小栈最大可以达到GB级依赖具体实现。在Go1.4以前Go的动态栈采用的是分段式的动态栈通俗地说就是采用一个链表来实现动态栈每个链表的节点内存位置不会发生变化。但是链表实现的动态栈对某些导致跨越链表不同节点的热点调用的性能影响较大因为相邻的链表节点它们在内存位置一般不是相邻的这会增加CPU高速缓存命中失败的几率。为了解决热点调用的CPU缓存命中率问题Go1.4之后改用连续的动态栈实现也就是采用一个类似动态数组的结构来表示栈。不过连续动态栈也带来了新的问题当连续栈动态增长时需要将之前的数据移动到新的内存空间这会导致之前栈中全部变量的地址发生变化。虽然Go语言运行时会自动更新引用了地址变化的栈变量的指针但最重要的一点是要明白Go语言中指针不再是固定不变的了因此不能随意将指针保持到数值变量中Go语言的地址也不能随意保存到不在GC控制的环境中因此使用CGO时不能在C语言中长期持有Go语言对象的地址 Go语言中函数还可以直接或间接地调用自己也就是支持递归调用。Go语言函数的递归调用深度逻辑上没有限制函数调用的栈是不会出现溢出错误的因为Go语言运行时会根据需要动态地调整函数栈的大小。每个goroutine刚启动时只会分配很小的栈4或8KB具体依赖实现根据需要动态调整栈的大小栈最大可以达到GB级依赖具体实现在目前的实现中32位体系结构为250MB,64位体系结构为1GB。在Go1.4以前Go的动态栈采用的是分段式的动态栈通俗地说就是采用一个链表来实现动态栈每个链表的节点内存位置不会发生变化。但是链表实现的动态栈对某些导致跨越链表不同节点的热点调用的性能影响较大因为相邻的链表节点它们在内存位置一般不是相邻的这会增加CPU高速缓存命中失败的几率。为了解决热点调用的CPU缓存命中率问题Go1.4之后改用连续的动态栈实现也就是采用一个类似动态数组的结构来表示栈。不过连续动态栈也带来了新的问题当连续栈动态增长时需要将之前的数据移动到新的内存空间这会导致之前栈中全部变量的地址发生变化。虽然Go语言运行时会自动更新引用了地址变化的栈变量的指针但最重要的一点是要明白Go语言中指针不再是固定不变的了因此不能随意将指针保持到数值变量中Go语言的地址也不能随意保存到不在GC控制的环境中因此使用CGO时不能在C语言中长期持有Go语言对象的地址
因为Go语言函数的栈不会溢出所以普通Go程序员已经很少需要关心栈的运行机制的。在Go语言规范中甚至故意没有讲到栈和堆的概念。我们无法知道函数参数或局部变量到底是保存在栈中还是堆中我们只需要知道它们能够正常工作就可以了。看看下面这个例子 因为Go语言函数的栈会自动调整大小所以普通Go程序员已经很少需要关心栈的运行机制的。在Go语言规范中甚至故意没有讲到栈和堆的概念。我们无法知道函数参数或局部变量到底是保存在栈中还是堆中我们只需要知道它们能够正常工作就可以了。看看下面这个例子
```go ```go
func f(x int) *int { func f(x int) *int {