【Go】go语言中的几点细节

Go是门简洁、高效的语言,因为在底层封装了许多东西,在go的编写中有许多细节需要我们留意,此文章尽可能通过代码来列举一些go语言中的细节(keng)。
1.

func main() {

	slice := []int{0, 1, 2, 3}
	m := make(map[int]*int)

	for key, val := range slice {
		m[key] = &val
	}

	for k, v := range m {
		fmt.Println(k, "->", *v)
	}
}
// 0 -> 3
// 1 -> 3
// 2 -> 3
// 3 -> 3

go中传递是值传递,对于for range slice 其实go在底层遍历的是slice的副本。对于变量key和val在遍历中是不会重新创建,也就是例子中只有一个变量key和val,在遍历中因为取了val的指针,遍历结束,val指针指向遍历最终值为3。

2.比较

  • go中的slice、map、函数是不可以比较的,除了和nil比较。
  • 函数和nil比较总返回false
  • sliec在var s []int 或 var s []int = nil声明时和nil比较会相等,其余比较一般不相等,map同理
  • struct 比较时编译器会根据双方的成员类型(如结构体中有slice类型则不可比较)、成员声明顺序来确定是否可以比较,在运行时根据成员具体值的比较返回比较值
  • 数组可以比较,编译器在编译期间会检查两个数组的成员类型(成员类型不能比较则不能比较)和成员数量,在运行时比较各成员的值

3

func main() {
    str := "hello"
    str[0] = 'x'
    fmt.Println(str)
}

golang中的字符串只读的,不能修改
4. defer

func increaseA() int {
    var i int
    defer func() {
        i++
    }()
    return i
}

func increaseB() (r int) {
    defer func() {
        r++
    }()
    return r
}

func main() {
    fmt.Println(increaseA())  // 0
    fmt.Println(increaseB())  // 1
}

defer延迟执行是go中重要的机制,defer执行在return之前,increaseA返回匿名变量,实际上在return i 时就执行s = i 将s作为函数结果传递出去了。increaseB的函数声明的返回参数和return中的一致,因此执行defer函数时会改变r的值,这里的r都是同一个变量。
5.

var p *int

func foo() (*int, error) {
    var i int = 5
    return &i, nil
}

func bar() {
    //use p
    fmt.Println(*p)
}

func main() {
    p, err := foo()
    if err != nil {
        fmt.Println(err)
        return
    }
    bar()
    fmt.Println(*p)
}

此段代码会造成运行时错误,原因在于:=对于不同作用域的相同变量会重新定义,就是main函数中的指针p会覆盖全局变量p,在执行bar函数时,此时全局指针p为nil,bar函数拿不到指针则报错崩溃。
6.

func main() {
    data := []int{1,2,3}
    i := 0
    ++i
    fmt.Println(data[i++])
}

上述代码会报错,原因在于go中不存在++i--i,对于i++它是一条语句,并不是一个表达式,这是go语言在自增方面与其他语言的不同。

你可能感兴趣的:(go)