前言
2020年了。
正文
问题来源
2019年6月我写了一篇博客讲的是golang语言中的陷阱,今天接着讲。
1. slice append问题:
问题:
func main() {
s := []int{5}
s = append(s, 7)
s = append(s, 9)
x := append(s, 11)
y := append(s, 12)
fmt.Println(s, x, y)
}
//结果是:[5 7 9] [5 7 9 12] [5 7 9 12]
分析:
这个问题设计到slice的扩容机制
请记住以下两条规则:
如果切片的容量小于1024个元素,那么扩容的时候slice的cap就翻番,乘以2;一旦元素个数超过1024个元素,增长因子就变成1.25,即每次增加原来容量的四分之一。
如果扩容之后,还没有触及原数组的容量,那么,切片中的指针指向的位置,就还是原数组,如果扩容之后,超过了原数组的容量,那么,Go就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。
当向slice添加元素时实际是在向数组中添加。如果添加的数据超过了数组cap的大小,就会重新申请一个更大的数组,将元素复制过去。这里的s的容量从1扩容到2,最后扩展为4。原本的cap值是4,追加元素没有超过4,所以一直都是在同一个数组里操作。
2. defer执行顺序
问题
func main(){
a := 1
b := 2
defer calc(a, calc(a,b))
a = 0
defer calc(a, calc(a,b))
}
func calc(x,y int){
fmt.Println(x,y,x+y)
return x+y
}
//结果
/*
1 2 3
0 2 2
0 2 2
1 3 4
*/
分析
考察两个知识点:
1.defer是栈调用,后写的先执行
2.defer的函数调用语句会在父函数调用后执行,但是用到的参数会在当时就执行得出
3. 反射type
问题
func main() {
i := GetValue()
switch i.(type) {
case int:
println("int")
default:
println("unknown")
}
}
func GetValue() int {
return 1
}
//执行错误
分析
编译失败,因为type只能使用在interface,将GetValue函数的返回值改为interface{}即可正确运行。
4. 接口与nil
问题
type People interface {
Show()
}
type Student struct{}
func (stu *Student) Show() {}
func live() People {
var stu *Student
return stu
}
func main() {
if live() == nil {
fmt.Println("AAAAAAA")
} else {
fmt.Println("BBBBBBB")
}
}
// 执行结果:BBBBBBB
分析
interface本身不为空,而是它的值(data为指针,并指向空)
判断接口指向的单元是否为空可以用下面的方法
func IsNil(i interface{}) bool {
vi := reflect.ValueOf(i)
//如果调用IsNil的不是一个指针,会出现异常,需要捕获异常。
if vi.Kind() == reflect.Ptr {
return vi.IsNil()
}
return false
}
5. defer返回值
问题
func main() {
println(DeferFunc1(1))
println(DeferFunc2(1))
println(DeferFunc3(1))
}
func DeferFunc1(i int) (t int) {
t = i
defer func() {
t += 3
}()
return t
}
func DeferFunc2(i int) int {
t := i
defer func() {
t += 3
}()
return t
}
func DeferFunc3(i int) (t int) {
defer func() {
t += i
}()
return 2
}
// 执行结果:
// 4
// 1
// 3
分析
考点:defer和函数返回值 需要明确一点是defer需要在函数结束前执行。 函数返回值名字会在函数起始处被初始化为对应类型的零值并且作用域为整个函数 DeferFunc1有函数返回值t作用域为整个函数,在return之前defer会被执行,所以t会被修改,返回4; DeferFunc2函数中t的作用域为函数,返回1; DeferFunc3返回3
5. defer返回值
问题
func main() {
println(DeferFunc1(1))
println(DeferFunc2(1))
println(DeferFunc3(1))
}
func DeferFunc1(i int) (t int) {
t = i
defer func() {
t += 3
}()
return t
}
func DeferFunc2(i int) int {
t := i
defer func() {
t += 3
}()
return t
}
func DeferFunc3(i int) (t int) {
defer func() {
t += i
}()
return 2
}
// 执行结果:
// 4
// 1
// 3
分析
考点:defer和函数返回值 需要明确一点是defer需要在函数结束前执行。 函数返回值名字会在函数起始处被初始化为对应类型的零值并且作用域为整个函数 DeferFunc1有函数返回值t作用域为整个函数,在return之前defer会被执行,所以t会被修改,返回4; DeferFunc2函数中t的作用域为函数,返回1; DeferFunc3返回3
6. 结构体比较
问题
func main() {
sn1 := struct {
age int
name string
}{age: 11, name: "qq"}
sn2 := struct {
age int
name string
}{age: 11, name: "qq"}
if sn1 == sn2 {
fmt.Println("sn1 == sn2")
}
sm1 := struct {
age int
m map[string]string
}{age: 11, m: map[string]string{"a": "1"}}
sm2 := struct {
age int
m map[string]string
}{age: 11, m: map[string]string{"a": "1"}}
if sm1 == sm2 {
fmt.Println("sm1 == sm2")
}
}
//sn1 == sn2,但后面比较编译异常
分析
考点:结构体比较
进行结构体比较时候,只有相同类型的结构体才可以比较,结构体是否相同不但与属性类型个数有关,还与属性顺序相关。
sn3:= struct {
name string
age int
}{age:11,name:"qq"}
sn3与sn1就不是相同的结构体了,不能比较。 还有一点需要注意的是结构体是相同的,但是结构体属性中有不可以比较的类型,如map,slice。 如果该结构属性都是可以比较的,那么就可以使用“==”进行比较操作。
可以使用reflect.DeepEqual进行比较
if reflect.DeepEqual(sn1, sm) {
fmt.Println("sn1 ==sm")
}else {
fmt.Println("sn1 !=sm")
}
所以编译不通过: invalid operation: sm1 == sm2
6. iota 穿插
问题
const (
x = iota
y
z = "zz"
k
p = iota
)
func main() {
fmt.Println(x,y,z,k,p)
}
//0 1 zz zz 4
分析
考点:iota
记住
总结:
勤思考。
结语
不管怎么样好好加油。