Golang 易错点

1.go传值还是传引用

  • Go语言并不存在类似其他语言的引用类型(没有开辟新的内存地址,则是引用,切片没有扩容,指针没有new 或者&{}
  • 无论参数是普通类型还是指针类型都会发生参数值拷贝(变量值,指针值都是新的)
  • 函数内对切片进行了append,需要将切片作为返回值返回

2.结构地指针是传值还是传引用

  • 指针的值会改变,但是指向的内存空间还是相同的,若指针没有开辟新的内存地址,则传引用,开辟的话,开辟后改变值,不会影响外面

func TestAdd(tt *testing.T) {
	t := &T{
		FieldA: "a",
		FieldB: "b",
	}

    //Before changing: value-&{a b}, address-0xc000164088
	fmt.Printf("Before changing: value-%v, address-%p\n", t, &t)
	
	
	changeTypeField(t)
	//After changeTypeField: value-&{A B}, address-0xc000164088
	fmt.Printf("After changeTypeField: value-%v, address-%p\n", t, &t)
	
	changeTypeStruct(t)
	//After changeTypeStruct: value-&{A B}, address-0xc000164088
	fmt.Printf("After changeTypeStruct: value-%v, address-%p\n", t, &t)

}

//这里p 是局部变量,值是新的,但指针指向同一片内存,所以外面发生变化
func changeTypeField(p *T) {
	p.FieldA = "A"
	p.FieldB = "B"
	fmt.Printf("In changeTypeField: value-%v, address-%p\n", p, &p)
	// value-&{A B}, address-0xc000164090
}

//p 是局部变量,p的值是新的,但是开辟新的内存地址,所以改变里面内容,不会影响外面
func changeTypeStruct(p *T) {
	p = &T{
		FieldA: "Alpha",
		FieldB: "Beta",
	}
	fmt.Printf("In changeTypeStruct: value-%v, address-%p \n", p, &p)
}

Before changing: value-&{a b}, address-0xc000164088
In changeTypeField: value-&{A B}, address-0xc000164090
After changeTypeField: value-&{A B}, address-0xc000164088
In changeTypeStruct: value-&{Alpha Beta}, address-0xc000164098 
After changeTypeStruct: value-&{A B}, address-0xc000164088

3.切片传指针还是传引用

  • 切片底层由数组指针 + len(长度) +cap(容量) 组成
  • 需要切片、数组分开看,局部切片是len、cap传值,若没有扩容,cap还有容量,若元素改变,局部切片的长度会改变,外面的切片长度不会改变,但是数组内容改变
  • 若cap不够,发生扩容,局部切片数组指针指向新的内存地址、len、cap 发生改变,局部切片是新的,外部切片不受影响

func TestTest(t *testing.T) {
	var s []int
	for i := 1; i <= 3; i++ {
		s = append(s, i)
	}
	//此时len=3 cap=4  [1 2 3] before
	fmt.Println(s, "before")
	
	//RRR  [1 2 3] 3 4 
	fmt.Printf("RRR \x1B[1;32;32m %+v %d %d \n", s, len(s), cap(s))
	reverse(s)
	
	//[1 2 3] after
	fmt.Println(s, "after")
	
	//RRR  [1 2 3] 3 4 
	fmt.Printf("RRR \x1B[1;32;32m %+v %d %d \n", s, len(s), cap(s))
	
}

func reverse(s []int) {
	s = append(s, 999,1000)
	for i, j := 0, len(s)-1; i < j; i++ {
		j = len(s) - (i + 1)
		s[i], s[j] = s[j], s[i]
	}
	
	//RRR  [1000 999 3 2 1] 5 8 
	fmt.Printf("RRR \x1B[1;32;32m %+v %d %d \n", s, len(s), cap(s))
}

4.发生错误时使用defer关闭一个文件

如果你在一个for循环内部处理一系列文件,你需要使用defer确保文件在处理完毕后被关闭:

  • defer仅在函数返回时才会执行,在循环的结尾或其他一些有限范围的代码内不会执行。
for _, file := range files {
    if f, err = os.Open(file); err != nil {
        return
    }
    // 这是错误的方式,当循环结束时文件没有关闭
    defer f.Close()
    // 对文件进行操作
    f.Process(data)
}
但是在循环结尾处的defer没有执行,所以文件一直没有关闭!垃圾回收机制可能会自动关闭文件,但是这会产生一个错误,更好的做法是:

for _, file := range files {
    if f, err = os.Open(file); err != nil {
        return
    }
    // 对文件进行操作
    f.Process(data)
    // 关闭文件
    f.Close()
 }

5.误用短声明导致变量覆盖

  • if 语句内若有跟外部变量有关系的变量,不要使用 :=声明
  • err 也是如此,if内 避免使用 if _,err:=SomeFunc(){}
var remember bool = false
if something {
    remember := true //错误
}
// 外面remember还是为false,
//因为 if语句内remember被重新声明&&赋值了

//所以正确的写法应该是:
if something {
    remember = true
}

参考资料:https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/directory.md

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值