1 流程控制
这里只讲"for range 语句"这个关键字,主要用于遍历,用来遍历数组、slice、map、chan。
例如:
package main
import (
"fmt"
)
func main() {
str := "hello world,中国"
for i, v := range str {
fmt.Printf("index[%d] val[%c]\n", i, v)
}
}
2 函数
函数实际上在上一节也有提过。
2.1. 声明语法:func 函数名 (参数列表) [(返回值列表)] {}
例如
func add(){
}
2.2. golang函数特点:
a. 不支持重载,一个包不能有两个名字一样的函数。
b.函数也是一种类型,一个函数可以赋值给变量。
c. 可以是匿名函数。
d. 多返回值。
函数的例子1,形参带有回调函数。
package main
import "fmt"
// 定义一个函数指针,和C/C++的typedef一样
type op_func func(int, int) int
func add(a, b int) int {
return a + b
}
func sub(a, b int) int {
return a - b
}
// 定义一个函数,这个函数的形参带有回调函数
func operator(op op_func, a, b int) int {
return op(a, b)
}
func main() {
var a, b int
add(a, b)
var c op_func // 自定义类型的变量c。
c = add // 将函数赋值给变量c。
fmt.Println(add)
fmt.Println(c)
sum := operator(c, 100, 200)
fmt.Println(sum)
}
函数的例子2,函数的返回值若只写return,则会按照函数的返回值声明进行返回。
package main
import "fmt"
func calc(a, b int) (sum int, avg int) {
sum = a + b
avg = (a + b) / 2
return // 这里虽然没返回,但是实际上会按照上面的返回值的声明进行返回。相当于语句:return sum, avg
}
func main() {
sum, avg := calc(10, 20)
fmt.Println("sum: ", sum, ", avg: ", avg)
}
2.3. 函数参数传递方式(值传递、引用传递):
1). 值传递。
2). 引用传递。
注意1:无论是值传递,还是引用传递,传递给函数的都是变量的副本,不过,值传递是值的拷贝。引用传递是地址的拷贝,一般来说,地址拷贝更为高效。而值拷贝取决于拷贝的对象大小,对象越大,则性能越低。
并且由于地址拷贝,它们会共用同一片内存,所以在函数内部修改返回后,回到调用函数再进行使用,该变量也是发生变化了的。
注意2:map、slice、chan、指针、interface默认以引用的方式传递。
2.4. 命名返回值的名字
看上面的函数的例子2。
2.5. _标识符,用来忽略返回值
func calc(a, b int) (sum int, avg int) {
sum = a + b
avg = (a +b)/2
return
}
func main() {
sum, _ := calc(100, 200)
}
2.6. 可变参数:
func add(arg…int) int { //0个或多个参数
}
func add(a int, arg…int) int { // 1个或多个参数
}
func add(a int, b int, arg…int) int { // 2个或多个参数
}
注意:其中arg是一个slice,我们可以通过arg[index]依次访问所有参数,通过len(arg)来判断传递参数的个数,但是len(arg)的范围只是arg,不包含已经确定的参数,例如上面代码的"1个或多个参数",前面的a形参是不在这个长度len范围。
例如:
package main
import "fmt"
func add(a int, arg ...int) int {
var sum int = a
for i := 0; i < len(arg); i++ {
sum += arg[i]
}
return sum
}
func concat(a string, arg ...string) (result string) {
result = a
for i := 0; i < len(arg); i++ {
result += arg[i]
}
return
}
func main() {
sum := add(10, 3, 3, 3, 3)
fmt.Println(sum)
res := concat("hello", " ", "world")
fmt.Println(res)
}
3. defer用途:
1. 当函数返回时,执行defer语句。因此,可以用来做资源清理。
2. 多个defer语句,按先进后出的方式执行,与栈的原理一样。
3. defer语句中的变量,在defer声明时就决定了它的值。
3.1 常见清理资源的操作:
// 1 关闭文件句柄:
func read() {
file := open(filename)
defer file.Close()
//文件操作
}
// 2. 锁资源释放
func read() {
mc.Lock()
defer mc.Unlock()
//其他操作
}
// 3. 数据库连接释放
func read() {
conn := openDatabase()
defer conn.Close()
//其他操作
}
3.2 defer的例子1:
package main
import "fmt"
// 定义一个全局的函数变量
var (
result = func(a1 int, b1 int) int {
return a1 + b1
}
)
func test(a, b int) int {
// 这里定义了一个局部的函数变量,所以实际上test和全局变量result的作用是一样的
result := func(a1 int, b1 int) int {
return a1 + b1
}
return result(a, b)
}
func main() {
// 1. 调用全局函数变量
fmt.Println(result(100, 200))
var i int = 0
defer fmt.Println(i) // 有defer的语句会最后执行,但是用到的变量在这里声明时就决定了,所以这里最后输出:0
defer fmt.Println("second") // 先进后出,所以两个defer最后必定是输出:second后,再输出 0
i = 10
fmt.Println(i)
}
3.3 例子2:
func f() {
// 相当于执行了5次defer,然后根据先进后出的情况,那么就是输出 4 3 2 1 0
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
}