go 表达式 —— 摘自go语言学习笔记

  1. 位移右操作数必须是无符号整数,或可以转换的无显式类型常量
    func main() {
    	b := 23          //b是有符号int类型变量
    	x := 1 << b      //无效操作,1 << b (shift count type int,must be unsigned integer)
    }
    
  2. 如果是非常量位移表达式,那么会优先将无显式类型的常量左操作数转型
    func main() {
    	a := 1.0 << 3    //常量表达式(包括常量展开)
    	fmt.Printf("%T,%v\n", a, a) //int, 8
    	var s uint = 3
    	b := 1.0 << s   //无效操作,因为b没有提供类型,那么编译器根据1.0推断,显然无法对浮点数做位移操作
    	var c int32 = 1.0 << s   //自动将1.0转换为int32类型
    	fmt.Printf("%T,%v\n", c, c)   //int32, 8
    }
    
  3. 指针类型支持相等运算符,但不能做加减法运算和类型转换。如果两个指针指向同一地址,或都为nil,那么它们相等。可通过unsafe.Pointer将指针转换为uintptr后进行加减法运算,但可能会造成非法访问。Pointer类似C语言中的void *万能指针,可用来转换指针类型。它能安全持有对象或对象成员,但uintptr不行。后者近视一种特殊整型,并不引用目标对象,无法阻止垃圾回收器回收对象内存
  4. 零长度对象的地址是否相等和具体的实现版本有关,不过肯定不等于nil。即便长度为0,该对象依然是“合法存在”的,拥有合法内存地址,这与nil语义完全不同。在runtime/malloc.go里有个zerobase全局变量,所有通过mallocgc分配的零长度对象都使用该地址。当然也可能在栈上分配,不调用mallocgc函数
  5. 对复合类型(数组,切片,字典,结构体)变量初始化时,初始化表达式必须含类型标签;左花括号必须在类型尾部,不能另起一行;多个成员初始值以逗号分隔;允许多行,但每行须以逗号或右花括号结束
    type data struct {
    	x int
    	s string
    }
    var a data = data{1, "abc"}
    b := data {
    	1,
    	"abc",
    }
    c := []int {
    	1,
    	2 }
    d := []int {1, 2,
    	3, 4,
    	5,
    }
    
  6. 死代码是指永远不会被执行的代码,可使用专门的工具,或用代码覆盖率测试进行检查。某些比较智能的编译器也可主动清除死代码
  7. 对于某些过于复杂的组合条件,建议将其重构为函数,函数调用虽然有一些性能损失,可却让主流程变得更加清爽。条件语句独立之后,更易于测试,同样会改善代码可维护性
  8. switch语句
    8.1
    func main() {
    	switch x {
    	case a, b:    
    		println()     //多个匹配条件命中其一即可(OK)
    	}
    }
    
    8.2 条件表达式支持非常量值
    8.3 switch同样支持初始化语句,按从上到下,从左到右顺序匹配case执行。只有全部匹配失败时,才会执行default块
    func main() {
    	switch x := 5; x {
    	default:         // 编译器确保不会先执行default块
    		x += 100
    		println(x)
    	case 5:
    		x += 50    
    		println(x)     
    	}
    }
    // 输出:55
    
    8.4 相邻的空case不构成多条件匹配
    func main() {
    	switch x {
    	case a:      // 单条件,内容为空。隐式“case a: break;”
    	case b:    
    		println("b")     
    	}
    }
    
    8.5 不能出现重复的case常量值
    func main() {
    	switch x := 5; x {
    	case 5:
    		println("a")     
    	case 6, 5:     // 错误:duplicate case 5 in switch
    		println("b")     
    	}
    }
    
    8.6 无须显式执行break语句,case执行完毕后自动中断。如需贯通后续case(源码顺序),须执行fallthrough,但不再匹配后续条件表达式
    func main() {
    	switch x := 5; x {
    	default:
    		println(x)
    	case 5:
    		x += 5
    		println(x) 
    		
    		fallthrough    // 继续执行下一case,但不再匹配条件表达式    
    	case 6: 
    		x += 20   
    		println(x)
    
    		fallthrough  // 如果在此继续fallthrough,不会执行default,完全按源码顺序
    		             // 导致“cannot fallthrough final case in switch”错误    
    	}
    }
    //输出: 15 35
    
    8.7 注意,fallthrough必须放在case块结尾,可使用break语句阻止
    func main() {
    	switch x := 5; x {
    	default:
    		println(x)
    	case 5:
    		x += 5
    		println(x) 
    		
    		if x >= 15 {
    			break
    		}
    		fallthrough    // 继续执行下一case,但不再匹配条件表达式    
    	case 6: 
    		x += 20   
    		println(x)  
    	}
    }
    
    8.8 某些时候,switch还被用来替换if语句。被省略的switch条件表达式默认值为true,继而与case比较表达式结果匹配
    func main() {
    	switch x := 5; {
    	case x > 5:
    		println("a") 
    	case x > 0 && x <= 5:    // 不能写成“case x > 0, x <= 5”,因为多条件是or关系 
    		println("b")   
    	default:
    		println("z")
    	}
    }
    //输出: b
    
  9. for 语句
    9.1 条件表达式中如有函数调用,须确认是否会重复执行。可能会被编译器优化掉,也可能是动态结果须每次执行确认
    func count() int {
       print("count.")
       return 3
    }
    func main() {
       // 多变量不能分开写,比如i := 0, c := count(), 编译报错
       for i, c := 0, count(); i < c; i++ {         // 初始化语句的count函数仅执行一次
       	println("a", i)
       }
       
       c := 0
       for c < count() {       //条件表达式中的count重复执行
       	println("b", c)
       	c++
       }
    }
    // 输出:
    // count.a 0
    // a 1
    // a 2
    
    // count.b 0
    // count.b 1
    // count.b 2
    
    9.2 for … range 迭代
    for range data {   // 仅迭代,不返回,可用来执行清空channel等操作
    }
    
    9.3 range会复制目标数据
    func main() {
    	data := [3]int{10, 20, 30}
    
    	for i, x := range data {    // 从data复制品中取值
    		if i == 0 {
    			data[0] += 100
    			data[1] += 200
    			data[2] += 300
    		}
    		fmt.Printf("x:%d, data:%d\n", x, data[i])
    	}
    
    	for i, x := range data[:] {   //仅复制slice,不包括底层array
    		if i == 0 {
    			data[0] += 100
    			data[1] += 200
    			data[2] += 300
    		}
    		fmt.Printf("x:%d, data:%d\n", x, data[i])
    	}
    }
    
    // 输出:
    // x:10, data:110
    // x:20, data:220          // range返回的依旧是复制值
    // x:30, data:330
    
    // x:110, data:210        // 当i==0修改data时,x已经取值,所以是110
    // x:420, data:420        // 复制的仅是slice自身,底层array依旧是原对象
    // x:630, data:630
    
    
    //注意,对string对象进行[:]类型不会转成切片
    var s = "spf"
    fmt.Printf("%T, %v\n", s, s)     // string, spf
    fmt.Printf("%T, %v\n", s[:], s[:])     // string, spf
    
    9.4 for range遍历汉字
    func main() {
    s := "abc汉字"
    
    for i := 0; i < len(s); i++ { // byte
        fmt.Printf("%c,", s[i])
    }
    
    fmt.Println()
    
    for _, r := range s { // rune
        fmt.Printf("%c,", r)
    }
    }
    // a,b,c,,±,,,,,
    // a,b,c,汉,字,
    
  10. goto不能跳转到其他函数或内层代码块内
    func test() {
    test:
    	println("test")
    }
    func main() {
    	for i := 0; i< 3; i++ {
    		loop:
    			println(i)
    	}
    	
    	goto test    // 错误:label test not defined
    	goto loop    // 错误:goto loop jumps into block
    }
    
  11. break 可用于 for、switch、select,而 continue 仅能用于 for 循环
  12. 根据调用者不同,方法分为两种表现形式:
    instance.method(args…) —> .func(instance, args…)
    前者称为 method value,后者 method expression。
    两者都可像普通函数那样赋值和传参,区别在于 method value 绑定实例,而 method expression 则须显式传参。
    type User struct {
        id int
        name string
    }
    
    func (self *User) Test() {
        fmt.Printf("%p, %v\n", self, self)
    }
    
    func main() {
        u := User{1, "Tom"}
        u.Test()
    
        mValue := u.Test
        mValue() // 隐式传递 receiver
    
        mExpression := (*User).Test
        mExpression(&u) // 显式传递 receiver
    }
    输出:	
    0x210230000, &{1 Tom}
    0x210230000, &{1 Tom}
    0x210230000, &{1 Tom}
    
    需要注意,method value 会复制 receiver。
    type User struct {
        id int
        name string
    }
    
    func (self User) Test() {
        fmt.Println(self)
    }
    
    func main() {
        u := User{1, "Tom"}
        mValue := u.Test // 立即复制 receiver,因为不是指针类型,不受后续修改影响。
    
        u.id, u.name = 2, "Jack"
        u.Test()
    
        mValue()
    }
    输出:
    {2 Jack}
    {1 Tom}
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值