defer
记住两个性质: 1、同栈,后进先出 2、在推迟执行时已经求值,不是在调用时才求职。 (官网例子说明)
func trace(s string) string { fmt.Println("entering:", s) return s } func un(s string) { fmt.Println("leaving:", s) } func a() { defer un(trace("a")) fmt.Println("in a") } func b() { defer un(trace("b")) fmt.Println("in b") a() } func main() { b() }
input result entering: b in b entering: a in a leaving: a leaving: b
内存分配
Go与C不同,该局部变量对应的数据在函数返回后依然有效。实际上,每当获取一个复合字面的地址时,都将为一个新的实例分配内存。
new 分配内存,不会初始化内存,只会将内存置零,返回指针
make 分配内存,已初始化,用于创建slice,map,channel,不返回指针
打印格式
fmt.Printf. %v --> value, 任意值,甚至是array,struct, map
%+v --> 为结构体的每个字段加上字段名
%#v --> 完全按照Go的语法打印值
%T --> 打印值的类型
...用法
...形参可以指定具体类型,代表这个类型元素还有很多
func append(slice [] T, element ...T)
append(slice, element1,elment2)
append(slice, slice2...)
值方法和指针方法
如果实现了接收者是值类型的方法,会隐含地也实现了接收者是指针类型的方法。
如果方法的接收者是值类型,无论调用者是对象还是对象指针,修改的都是对象的副本,不影响调用者;如果方法的接收者是指针类型,则调用者修改的是指针指向的对象本身。
使用指针作为方法的接收者的理由:
方法能够修改接收者指向的值。
避免在每次调用方法时复制该值,在值的类型为大型结构体时,这样做会更加高效。
如果类型具备“原始的本质”,也就是说它的成员都是由 Go 语言里内置的原始类型,如字符串,整型值等,那就定义值接收者类型的方法。像内置的引用类型,如 slice,map,interface,channel,这些类型比较特殊,声明他们的时候,实际上是创建了一个 header, 对于他们也是直接定义值接收者类型的方法。这样,调用函数时,是直接 copy 了这些类型的 header,而 header 本身就是为复制设计的。
如果类型具备非原始的本质,不能被安全地复制,这种类型总是应该被共享,那就定义指针接收者的方法。比如 go 源码里的文件结构体(struct File)就不应该被复制,应该只有一份实体。
这一段说的比较绕,大家可以去看《Go 语言实战》5.3 那一节。参考: https://qcrao91.gitbook.io/go/interface/zhi-jie-shou-zhe-he-zhi-zhen-jie-shou-zhe-de-qu-bie
协程
func Serve(queue chan *Request) { for req := range queue { sem <- 1 go func() { process(req) // Buggy; see explanation below. <-sem }() } } func Serve(queue chan *Request) { for req := range queue { sem <- 1 go func(req *Request) { process(req) <-sem }(req) } }
req在每次for循环的go协程中被共享,不能保证在每个协程是唯一的,所以要做协程参数。 --->闭包概念