Go基本语法
Go基本语法
易错语法
- 无分号
- 定义了的变量一定要用
- 注意for循环不能加括号()
- else 语句应该与 if 语句在同一行,或者在 if 语句的右大括号之后。
- string不可修改
- 全局变量只能显式定义(必须写明类型)
变量
显式类型,定义了而未使用会报错
// 单行(有时候type可以自动推导出来)
var name type
// 多变量
var(
name1 type1
name2 type2
)
// 未定义变量可以用 := 自动推导
name := "ct"
// 变量交换
b, a = a, b
// 匿名变量:可抛弃,可以重复用
_ := test()
// 常量
const NAME type
// iota:const出现时被重置位0,出现一个常量就++
const (
a = iota //0
b //1
c //2
d = "111" // "111" iota 3
e // "111" iota 4
f = 100 //iota 5
g //100 iota 6
h = iota //iota 7
i //iota 8
)
const(
j = iota //0
k //1
)
IO
fmt.Scanf()
fmt.Scan()
fmt.Scanln()
fmt.Printf()
fmt.Print()
fmt.Println()
switch
// fallthrough:直接穿透下一个case。无视当前结果,强制执行后面的var tt int = 10
switch tt {
case 9:
fmt.Printf("case9: tt=%d\n", tt)
fallthrough
case 10:
fmt.Printf("case10: tt=%d\n", tt)
fallthrough //fallthrough: continue run next case
case 8:
fmt.Printf("case8: tt=%d\n", tt)
case 7:
fmt.Printf("case7: tt=%d\n", tt)
case 5:
if (a == 10) {
break;
}
fmt.Printf("case5: tt=%d\n", tt)
}
for
注意for循环不能加括号()
for i := 1; i <= 10; i++ {
fmt.Printf("i=%d ", i)
}
//for range
str := "TT fish"
for i, v := range str { //i下标,v对应值
fmt.Printf("%d: %c\n", i, v)
}
函数
基础函数
// func 名字 (参数) (返回类型)
//func name(par1 type1, par2 type2...) (return_type_1, return_type_2)
func cal(a int, b int, s string) (int, int, string) {
return a + b, a * b, s
}
//main里面:
x, y, s := cal(2, 3, "tt")
//可变参数(传入数量待定),如果还有固定参数的混合用的话,可变参数要放在最后,且只有一个可变参数
func calsum(a ...int) (...) {}
匿名函数
只用一次没有名字
//匿名函数
func() {
fmt.Printf("anonymous\n")
}()
a, b := func(a int, b int) (int, int) {
fmt.Printf("infunc: a + b = %d\n", a + b)
return a, b
}(4, 5)
fmt.Printf("outfunc: a = %d, b = %d\n", a, b)
高阶函数(回调函数)
把函数作为参数,这样同类型的函数就可以直接复合传参使用了
func add (a int, b int) (int) {
return a + b
}
//其它函数略,总之保持类型一样就好
func operator(a int, b int, f func(int, int)(int)) int {
return f(a, b)
}
//main里面:
a := 9
b := 3
fmt.Printf("add: %d\n", operator(a, b, add))
fmt.Printf("del: %d\n", operator(a, b, del))
fmt.Printf("mul: %d\n", operator(a, b, mul))
fmt.Printf("div: %d\n", operator(a, b, div))
闭包
有点绕
闭包:函数套函数,内层修改了外层函数的局部变量,且外层函数返回值是内层函数,则构成闭包
局部变量生命周期:正常的局部变量会随着函数的结束而销毁,而闭包的外层函数的局部变量不会随着外层函数的结束而销毁,因为内层函数仍在使用
//闭包
func inc() (func() int) {
i := 0
f := func() int {
i++
return i
}
return f
}
func main() {
r1 := inc()
fmt.Println(r1)
v1 := r1()
fmt.Printf("%d ", v1)
v2 := r1()
fmt.Printf("%d ", v2)
fmt.Printf("%d ", r1())
fmt.Printf("%d ", r1())
fmt.Printf("%d\n", r1())
fmt.Println("----------")
r2 := inc() //重新调用
v3 := r2()
fmt.Printf("%d ", v3)
fmt.Printf("%d ", r1()) //r1仍不变
fmt.Printf("%d\n", r2())
}
/*输出:
0xee9f60
1 2 3 4 5
----------
1 6 2
*/
数组 切片
值传递:array,单变量
引用传递:slice,map,chan
//数组定义
arr := [4] int {1, 2, 3, 4} //大小固定
//切片定义
arr2 := [] int {1, 2, 3, 4} //大小可变
异常
defer
用法:通常是打开一个资源或者文件之后立马defer,以便及时释放
//defer之后的会被延迟到最后执行,且函数执行到最后时,defer语句按栈顺序执行
//defer之后跟函数,则参数已被传入,只是最后再执行
fmt.Printf("0 ")
defer fmt.Printf("3 ")
defer fmt.Printf("4 ")
fmt.Printf("1 ")
defer fmt.Printf("5 ")
fmt.Printf("2 ")
//输出: 0 1 2 5 4 3
异常处理
defer + recover 捕获和处理异常
func testErr() {
defer func() {
err := recover()
if (err != nil) {
fmt.Println("err = ", err)
}
}()
a := 10
b := 0
t := a / b //这个以后的都不会正常执行,直接跳转到defer,之后返回
fmt.Printf("ans = %d\n", t)
fmt.Printf("tt\n")
}
//main里面...
testErr()
fmt.Printf("tt2\n") //这个可以正常执行
自定义错误
errors.New("...")
并发 goroutine
sync
并发执行,无需通信
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func download(url string) {
fmt.Println("start to download", url)
time.Sleep(time.Second) // 模拟耗时操作
wg.Done() //计数--
}
func main() {
for i := 0; i < 3; i++ {
wg.Add(1) //计数+1
go download("a.com/" + string(i+'0')) //go 关键字用于启动一个新的 goroutine
}
wg.Wait() //等待所有的协程执行结束。
fmt.Println("Done!")
}
channel
可以在协程之间传递消息。阻塞等待并发协程返回消息。
(所以大概是一个进程执行完之后才会进行下一个吗有点疑惑O.O
var ch = make(chan string, 10) // 创建大小为 10 的缓冲信道
func download(url string) {
fmt.Println("start to download", url)
time.Sleep(time.Second)
ch <- url // 将 url 发送给信道
}
func main() {
for i := 0; i < 3; i++ {
go download("a.com/" + string(i+'0'))
}
for i := 0; i < 3; i++ {
msg := <-ch // 等待信道返回消息。
fmt.Println("finish", msg)
}
fmt.Println("Done!")
}
单元测试
牛啊直接测名字后面加上_test就行
感觉这个功能很方便写题(赞美
// main_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
if ans := add(1, 2); ans != 3 {
t.Error("add(1, 2) should be equal to 3")
} else {
t.Log("Test 1 passed")
}
if ans := add(2, 2); ans != 4 {
t.Error("add(2, 2) should be equal to 4")
} else {
t.Log("Test 2 passed")
}
}
包和模块
这块折磨了很久(感觉自己很笨😭
详见这篇Bug解决:https://www.cnblogs.com/CTing/p/18716582
和原理:https://geektutu.com/post/quick-golang.html#9-包-Package-和模块-Modules