内容
1 切片与数组
2 defer
3 make与new
4 方法与函数
1 切片和数组
- 数组和结构体都是值变量,即:如果把一个数组变量和结构体变量赋值给另外的变量,是拷贝了一份值,两者的修改互不影响;
2 . go通过切片生成另外一个切片时,两个切片共享同一个底层数组,对其中一个修改元素时,两个都会改变;
例如:
a=[]int{1,2,3}
b:=a[:]
b[0] = 2
printLn(a[0]) —》输出:2
- 特别注意初始化切片时,如果指定了切片的长度,go会用nil来填充这个切片, 如果是基本类型则用基本类型零值填充,此后对切片通过append操作时,会在后面进行填充;所以在初始化一个切片并且指定了容量时,要注意长度初始化为0;
a := []int{1,2,3}
b := make([]int, 0, len(a)) //Yes
B := make([]int, len(a), len(a)) //No
B1 := make([]int, 0) //Yes
b1 := make([]int, 4) //No
2 defer
1.对于defer,当代码运行到defer语句时,defer后要运行的函数的入参此时已经确定了(即:defer函数的入参函数此时就被执行了,而不是到了调用是才被执行),defer下面的语句对该参数做的修改对于函数无效;;
2.如果同一个函数中有多个defer,被推迟的函数按照先进后出的顺序执行(压栈出栈),即:最后一个defer会被第一个执行;
func main(){
b()
}
func un(s string) {
fmt.Println("leaving:", s)
}
func trace(s string) string {
fmt.Println("enter:", s)
return s
}
func b() {
defer un(trace("b"))
fmt.Println("in:", "b")
a()
}
func a() {
defer un(trace("a"))
fmt.Println("in:", "a")
}
打印:
enter: b
in: b
enter: a
in: a
leaving: a
leaving: b
3.defer 原理
- 编译期;
- 将
defer
关键字被转换runtime.deferproc
; - 在调用
defer
关键字的函数返回之前插入runtime.deferreturn
;
- 将
- 运行时:
-
runtime.deferproc
会将一个新的runtime._defer
结构体追加到当前 Goroutine 的链表头; -
runtime.deferreturn
会从 Goroutine 的链表中取出runtime._defer
结构并依次执行;
-
- 后调用的
defer
函数会先执行:- 后调用的
defer
函数会被追加到 Goroutine_defer
链表的最前面; - 运行
runtime._defer
时是从前到后依次执行;
- 后调用的
- 函数的参数会被预先计算;
- 调用
runtime.deferproc
函数创建新的延迟调用时就会立刻拷贝函数的参数,函数的参数不会等到真正执行时计算;
- 调用
3 make与new
make和new的区别:make只能用于slice map和channel,返回的是该类型初始化后的引用;new(T)返回的是指向该类型的指针。
type File struct{
name string
}
&File{} 《=》 new(File)
```
make只用于映射、切片和管道,并且不返回指针,如果要得到指针请使用new
```
var p *[]int = new([]int) // 得到指针
var v []int = make([]int, 0, 100)
var p *[]int = new([]int)
*p = make([]int, 100)
4 方法和函数
1、函数中如果入参是值参数,那么该函数只能接收值入参;如果方法中的接收器是值接收器,那么该方法可以接收值接收器和指针接收器,即:可以通过该类型接收器的值变量和指针变量调用方法;
2、函数中如果入参是指针参数,那么只能接收指针参数;如果方法中申明的接收器是指针接收器,那么该方法可以接收值接收器和指针接收器;
引用:
- 《go语言实现与设计》https://draveness.me/golang/
- 《effective go》 https://www.kancloud.cn/kancloud/effective/72214
有疑问加站长微信联系(非本文作者)