控制结构
- Go完全忽略了if switch 和 for 结构中的括号,看起来更加简洁一点
- 可以使用return来结束函数的执行,使用goto与标签来调整执行的位置
- runtime.GOOS上个常量,表示操作系统类型
- Abs()用来返回一个整形数字的绝对值
- isGreater()用来比较俩个整形数字的大小
- if 可以包含一个初始化语句(如:给一个变量赋值)(在初始化语句后方必须加上分号)
if val := 10; val<max{
// do something
}
多返回值函数的错误
在函数编程中,经常会使用多返回值的型式,多个一个错误类型error的返回,表示出现的错误。
如果没有出现错误,则error返回为nil,否则返回错误信息
switch结构
- go的switch可以接收任意形式的表达式,但case必须是相同类型
- case 可以同时测试多个可能符合的表达式 例如 case val1, val2, val3:
- defalut分支可以放在任意位置,但最好放在末尾
switch可以不提供任何被判断的值,然后在每个case分支中进行测试不同的条件
例如:
switch {
case i < 0:
f1()
case j == 0:
f2()
case k > 0:
f3()
}
switch 语句还可以包含一个初始化语句
switch result := calculate(); {
case result < 0:
...
case result > 0:
...
default:
// 0
}
swtich语句还可以用于type-switch来判断某个interface变量中实际存储的变量类型
for语句
for语句只含判断条件
func main() {
var i int = 5
for i >= 0 {
i = i - 1
fmt.Printf("The variable i is now: %d\n", i)
}
也可以把判断条件也去掉,这样会变成一个死循环
for-range结构
- for-range结构可以迭代任何一个集合
for ix,val := range col {}
- val始终是集合对应索引的值拷贝,因此他只是只读模式,即使修改了val的值,集合中的原值也不会被修改(如果val为指针,则会产生指针的拷贝,依旧可以修改集合中的原值)
函数
- 在go语言中,函数重载是不被允许的
- Go默认使用按值传递参数,如果想要按引用传递 可以在变量名前面添加 & 符号
- <其实引用传递也是按值传递。因为指针本身也是数据类型,内容本身就是指向的地址>
- 在函数调用时,像切片(slice),字典(map),接口(interface),通道(channel)这样的引用类型都是默认使用引用传递(即使没有显式的指出指针)
命名返回值和非命名返回值的区别:
- 命名返回值作为结果形参被初始化为相应类型的零值
- 命名返回值,返回时只需要一条简单的不带参数的return语句
- 即使使用命名返回值,也可以无视他明确返回
func getX2AndX3(input int) (int, int) {
return 2 * input, 3 * input
}
func getX2AndX3_2(input int) (x2 int, x3 int) {
x2 = 2 * input
x3 = 3 * input
// return x2, x3
return
}
改变外部变量
传递指针给函数不但可以节省内存,而且赋予了函数直接修改外部变量的能力
package main
import (
"fmt"
)
// this function changes reply:
func Multiply(a, b int, reply *int) {
*reply = a * b
}
func main() {
n := 0
reply := &n
Multiply(10, 5, reply)
fmt.Println("Multiply:", *reply) // Multiply: 50
}
传递变长参数
- 如果函数的最后一个参数是采用
...type
的形式,那么这个函数就可以处理一个变长的参数。<这个长度可以是0>(可以理解为存储在一个type类型的数组中) func myFunc(a,b,c ...int){}
这个函数可以接收任意个数的int类型的参数- 如果参数被存储在一个slice类型的变量s中,则可以通过a… 的形式来传递参数调用变参函数
如果想要达到不同类型的长参数 应该如果传递
- 使用结构
type Options struct {
par1 type1,
par2 type2,
…
}- 使用空接口
如果一个变长参数的类型没有被指定,可以使用默认的空接口interface{},可以接收任何类型的参数
这方案不仅可以用来长度未知的参数,还可以用于任何不确定类型的参数
func typecheck(…,…,values … interface{}) {
for _, value := range values {
switch v := value.(type) {
case int: …
case float: …
case string: …
case bool: …
default: …
}
}
}
对空接口的理解
- 接口:它是一组方法,但它也是一种类型
interface{}
类型是没有方法的接口- 所有类型都满足空接口
go中接口是由俩个指针组成的
- 指向类型相关信息的指针
- 指向数据相关信息的指针
defer关键字
- defer关键字类似于java中的finally语句块,他可以在函数返回前(执行return之后)执行某个语句
- 一般用于释放某些已分配的资源
package main
import "fmt"
func main() {
function1()
}
func function1() {
fmt.Printf("1")
defer function2()
fmt.Printf("2")
}
func function2() {
fmt.Printf("3")
}
//输出结果是 132
特殊情况,这是fmt.print()的坑,在i++后面打印出来是1
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
// 这里打印出来的 i是 0
当有多个defer行为被注册时,它们会以逆序执行(类似栈,即后进先出)
内置函数
名称 | 说明 |
---|---|
close | 用于管道通信 |
len、cap len | 用于返回某个类型的长度或数量(字符串、数组、切片、map 和管道);cap 是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map) |
new、make | new 和 make 均是用于分配内存:new 用于值类型和用户定义的类型,如自定义结构,make 用于内置引用类型(切片、map 和管道)。它们的用法就像是函数,但是将类型作为参数:new (type)、make (type)。new (T) 分配类型 T 的零值并返回其地址,也就是指向类型 T 的指针。它也可以被用于基本类型:v := new(int)。make (T) 返回类型 T 的初始化之后的值,因此它比 new 进行更多的工作(详见第 7.2.3/4 节、第 8.1.1 节和第 14.2.1 节)new () 是一个函数,不要忘记它的括号 |
copy、append | 用于复制和连接切片 |
panic、recover | 两者均用于错误处理机制 |
print、println | 底层打印函数(详见第 4.2 节),在部署环境中建议使用 fmt 包 |
complex、real、imag | 用于创建和操作复数(详见第 4.5.2.2 节) |
闭包
- 闭包函数保存并积累其中的变量的值,不管外部函数退出与否,它都能够继续操作外部函数中的局部变量
- 在必包中使用的变量,可以在必包函数体内声明,也可以在外部函数声明
package main
import "fmt"
func main() {
// 可以理解为f是指向 函数栈的指针,f没有销毁,则栈不会销毁,所以局部变量一直叠加。
var f = Adder()
fmt.Print(f(1), " - ")
fmt.Print(f(20), " - ")
fmt.Print(f(300))
}
func Adder() func(int) int {
var x int
return func(delta int) int {
x += delta
return x
}
}
结果:
1 - 21 - 321
计算函数执行时间
start := time.Now()
longCalculation()
end := time.Now()
delta := end.Sub(start)
fmt.Printf("longCalculation took this amount of time: %s\n", delta)