Thinking_Out_Loud

《Go程序设计语言》读书笔记#4

2018-09-12

第五章 函数

< P120 >

  • 函数传参都是值传递;在传递slice时,会复制一个slicestruct,包含了底层数组指针、lencap,虽然被调用函数可以改变底层数组,但是其中lencap等对调用者是不可见的,除非用指针或返回新值更新;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    func f1(val []int) {
    val = append(val, 123)
    }

    func f2(val []int) []int {
    val = append(val, 321)
    fmt.Println(len(val))
    return val
    }

    func main() {
    var val = make([]int, 0, 10)
    f1(val)
    val = f2(val)
    for i, v := range val {
    fmt.Printf("%d\t%d\n", i, v)
    }
    }
    // 1
    // 0 321
  • 函数的类型又称作函数签名(signature),参数以及返回值的变量名对签名无影响;

  • Go的栈是可变的,取决于内存大小, 可以增长到数G;

< P125 >

  • go的GC不会回收操作系统资源,如打开的文件、网络连接等,必须进行显式的回收;
  • 当所有返回值都是具名的,return的操作数可以忽略,称作裸返回(bare return);

< P134 >

  • 函数可以赋值,即函数可以作为参数传递;
  • %*s中的*修饰符可以指定字符串前有多少空格;

    1
    fmt.Printf("%*s", 5 , "text")

< P135 >

具名函数只能在包级定义;但是匿名函数可以在函数内部给函数变量赋值;但是如果需要递归调用,用:=定义函数变量会导致不能调用自身,可以尝试下面这种方式:

1
2
3
4
5
6
7
8
9
10
var visitAll func(items []string)
visitAll = func(items []string) {
for _, item := range items {
if !seen[item] {
seen[item] = true
visitAll(m[item])
order = append(order, item)
}
}
}

< P141 >

注意在使用闭包时捕获迭代局部变量的问题,应该用一个新的局部变量保存;

< P144 >

  • 在运行到defer语句时,deferred的函数参数值就确定了,只有函数的执行被延迟;
  • Deferred的函数在return之后执行;

< P151 >

Deferred的函数调用的runtime.Stack方法可以打印panic时的stack是因为Go的panic机制会先运行deferred functionunwind the stack