Go语言
1. 基本组成
组成元素: 关键字,标识符,常量,字符串,符号
关键字列表:
break;default;func;interface;select;case;defer;go;map;struct;chan;else;goto;package;switch;const;fallthrough;if;range;type;continue;for;import;return;var
预定义标识符
append;bool;byte;cap;close;complex;complex64;complex128;uint8;uint16;uint32;uint64;uintptr;uint;int8;int16;int32;int64;int;imag;false;true;copy;float32;float64;iota;len;make;new;nil;panic;print;println;real;recover;string;true;
包声明 package main
引用 import "fmt"
函数
func main(){//注意{不能单独放在一行
fmt.Println("")//单独语句后面不用加分号,多个语句同一行需要加分号
}
(程序入口点 init函数->main函数)
注释 c++风格
变量命名规则: 大写字母开头对包外可见,小写字母对包外不可见
变量声明: var age int = 0;
3. 运行或构建
go build
go run XX.go
go install XX.go
4. 语言类型
基本类型 bool, int/float/complex/uint, string
特别数字类型: byte, rune(~int32), uintptr(无符号整型,用于存放一个指针)
派生类型
a. 指针 pointer
b. 数组
c. struct
d. channel
e. 函数
f. 切片
g. interface
h. Map类型
5. 变量
a. 变量声明
i. 指定变量类型 var identifier type
ii. 根据值自行判断 var v_name = value
iii. 省略var: v_name := value, 只能在函数体中出现
注意:
var s string
s:="hello world"
错误
而
var s string
s, s1 := "hello" ,"world"
正确
这说明:= 左侧只需要存在新变量即可
iv. var (
a int
b bool
)
一般用于声明全局变量
package main
import "fmt"
var (
a int
)
func main(){
a := "123"
fmt.Println(a)
}
如果声明一个局部变量却没有使用,会得到编译错误!
b. 值类型,引用类型
基本类型属于值类型
引用类型: &i
c. _ 与python类似
6. 常量
const identifier [type] = value
例如 const b = "abc"
const b string = "abc"
const a, b = 1, 2
常量用作枚举:
const {
Unknown = 0
F = 1
M = 2
}
len(), cap(), unsafe, Sizeof()等内置函数只用常量作参数时得到的结果可以用来给常量赋值
iota在遇到const后自动赋值0,随后const体内每一行常量声明都使iota增加1
package main
import "unsafe"
const (
z = iota
a = "a"
b = len(a)
c = unsafe.Sizeof(a)
d
e = iota
f
g = 1<<iota
)
func main(){
println(z,a,b,c,d,e,f, g)//0 a 1 16 16 5 6 128
}
7 运算符
a. 算数运算符 +-*/%++--
b. 关系运算符 == != > < >= <=
c. 逻辑运算符 && || !
d. 位运算符 & | ^ << >>
e. 赋值运算符 = += -= *= /= %= <<= >>= &= ^= |=
f. &a 返回地址 *a 指针
var p *int
a := 4
p = &a
运算符优先级
^!
* / % << >> & &
+ - | ^
== != < <= >= >
&&
||
8. 控制语句
a. if 布尔表达式 {
XXX
}else{
}
注意: int类型不能直接当表达式内容
b. switch var1 {
case 1:
....
default:
...
}
或者
switch {
case 条件1:
....
default:
...
}
注意 case自带break,要继续执行,需要用fallthrough
switch len(a){
case 1:
switch{
case g > 128:
println(128)
case g > 64:
println(64)
default:
println(g)
}
default:
println("default A")
}
Type Switch
switch x.(type){
case typeA:
...}
c. select语句-用于通信的switch语句,每个case必须是一个通信操作,发送或者接受。select随机执行一个可以运行的case,如果没有case可运行则将阻塞
所有channel表达式都会被求值
所有被发送的表达式都会被求值
default可运行
不会重新对channel或者值计算
d. for支持break, goto, continue
for init;condition; post{}
for condition{}
for {}
for key,value := range oldMap {}
例子
package main import "fmt" func main(){ numbers := [6]int{1,2,3,10} for i, x := range numbers{ fmt.Printf("%d: %d\n", i, x) } }
9. 函数
func function_name ([parameter list])[return types]{}
例如
func max(num1, num2 int) int{}
func main(){}
func swap(x, y string)(string, string){}
函数传参-引用传参传指针
函数变量
例子
package main import "fmt" func main(){ numbers := [6]int{1,2,3,10} Square := func(x int)int{return x * x} for i, x := range numbers{ fmt.Printf("%d: %d\n", i, Square(x)) } }
输出:
0: 1
1: 4
2: 9
3: 100
4: 0
5: 0
函数闭包 匿名函数,可以直接使用函数内的变量(即使已经离开了函数作用域),类似generator,不必声明
package main import "fmt" func getSequence() func() int { i := 0 return func() int { i++ return i } } func main(){ nextNumber := getSequence() fmt.Println(nextNumber()) fmt.Println(nextNumber()) fmt.Println(nextNumber()) nextNumber = getSequence() fmt.Println(nextNumber()) fmt.Println(nextNumber()) }
输出
1
2
3
1
2
函数方法
方法具有接收者,接收者可以是命名类型或者结构体类型的值或者指针
func (variable_name variable_type) function_name() [return_type] {}
package main //import "fmt" type Circle struct{ radius float64 } func (c Circle) getArea() float64{ return 3.14 * c.radius * c.radius } func main(){ var c1 Circle c1.radius = 5 println(c1.getArea()) }
10. 变量作用域
函数内:局部变量
函数外:全局变量,可以在整个包甚至外部包被导出后使用
函数定义:形式参数,会作为函数局部变量来使用??
注意:for循环内部局部变量
package main func main(){ var i = 10 for i:= 0;i < 5;i++{ println(i) } println(i) }
输出
0
1
2
3
4
10
11. 数组
与切片相比,数组大小固定
var variable_name [SIZE] variable_type
var variable_name = [5] float32{1,2}
var variable_name = [...] float32 {1,3} 自动计算容量
package main func main(){ var arr = [...]float32 {-1,-2,-3,-4,-5} for i:= 0;i < 5;i++{ println(arr[i]) } }
多维数组
var variable_name [SIZE1][SIZE2][SIZE3]....[SIZEN] variable_type
初始化方法
package main
func prt(arr [3][2] float32){
for i:= 0;i < 3;i++{
println(arr[i][0])
}
}
func main(){
var arr = [...][2]float32 {{-1,-2},{-3,-4},{-5}}
prt(arr)
}
越界显示exit status 2
数组作为参数
void myFunc(param [10] int){}
void myFunc(param[3][2] int){}
注意数组与切片不同,需要传入确切大小
package main func prt(arr [3][2] float32){ for i:= 0;i < 3;i++{ println(arr[i][0]) } } func main(){ var arr = [...][2]float32 {{-1,-2},{-3,-4},{-5}} prt(arr) }
12. 指针
&a *p
空指针nil
数组指针
var pf *[MAX] type
指针数组
var ptr [MAX]*int
package main import "fmt" func main(){ var arr = [][]float32 {{-1,-2},{-3,-4},{-5}} var parr *[][]float32 = &arr var arrp = []*[]float32 {&arr[0], &arr[1], &arr[2]} println(arr) println(parr) println(arrp) fmt.Println(arr) fmt.Println(parr) fmt.Println(arrp) }
[3/3]0xc00007a000
0xc000004440
[3/3]0xc000004460
[[-1 -2] [-3 -4] [-5]]
&[[-1 -2] [-3 -4] [-5]]
[0xc00007a000 0xc00007a018 0xc00007a030]
指向指针的指针 **int
13 结构体
type struct_variable_type struct{
member definition;
member definition;
...
}
初始化
variable_name := struct_variable_type {value1, value2, ... valuen}
variable_name := struct_variable_type {key1: value1, key2: value2...}
结构体指针访问结构体成员也是使用"."
函数传递结构体时使用值传参!!
package main import "fmt" type Circle struct{ radius int; } func changeR(c Circle){ c.radius = 100 } func changeR2(pc *Circle){ pc.radius = 200 } func main(){ var c = Circle{10} fmt.Println(c) changeR(c) fmt.Println(c) changeR2(&c) fmt.Println(c) }
输出
{10}
{10}
{200}
package main import "fmt" type Circle struct{ radius int; } type Circles struct{ circle Circle; } func changeR(cs Circles){ cs.circle.radius = 100 } func main(){ var c = Circles{Circle{10}} fmt.Println(c) changeR(c) fmt.Println(c) }
{{10}}
{{10}}
14. 切片
数组的抽象,长度不固定,可以追加
var identifier [] type
var slice1 [] type = make([]type, len)
slice1:= make([]type, len)
make函数可用于创建切片,语法为 make([]T, length, capacity)
s:= arr[startInd:endInd]
len(slice)
cap(slice)
slice = append(slice, 2,3,4)
copy(slice1, slice2)
15. range 用于在for循环中迭代Array,slice, channel或者map(集合)的元素,在数组和切片中返回索引和值,在map中返回key value对
for k, v := range map{}
for i, num := range nums{}
16. Map 使用hash实现的
var map_variable map[key_type] value_type;
map_variable := make(map[key_type]valuet_type)
例如 mapa = make(map[string] string)
delete(map, key)
对不在集合中的key值输出默认元素
package main func main(){ countryCapitalMap := map[string]int{"France": 1, "Italy": 2} println(countryCapitalMap["China"]) }
输出0
17. 类型转换
typename(expression)
18. 接口
type interfae_name interface{
method_name1 [return_type1],
method_name2 [return_type2],
...
}
注意 实现的方法是以方法的形式,也即 func (var_name var_type) method_name (arg arg_type) [return_type]{}来写
package main import ( "fmt" ) type Phone interface { call() } type NokiaPhone struct { } func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!") } type IPhone struct { } func (iPhone IPhone) call() { fmt.Println("I am iPhone, I can call you!") } func main() { var phone Phone phone = new(NokiaPhone) phone.call() phone = new(IPhone) phone.call() }
19. 错误处理
a. 返回值中返回错误信息
func Sqrt(f float64) (float64, error) { if f < 0 { return 0, errors.New("math: square root of negative number") } // 实现 }
b. panic, recover
panic 主动抛出错误
recover 捕获错误
发生panic
后,程序会从调用panic
的函数位置或发生panic
的地方立即返回,逐层向上执行函数的defer
语句,然后逐层打印函数调用堆栈,直到被recover
捕获或运行到最外层函数。多个panic只会捕捉最后一个。
recover
用来捕获panic
,阻止panic
继续向上传递。recover()
和defer
一起使用,但是defer
只有在后面的函数体内直接被掉用才能捕获panic
来终止异常,否则返回nil
,异常继续向外传递。
package main import "fmt" func main(){ defer func(){ if err := recover() ; err != nil { fmt.Println(err) } }() defer func(){ panic("three") }() defer func(){ panic("two") }() panic("one") }
20. 并发
a. goroutine: 轻量级线程,同一个程序中的所有goroutine共享一个地址空间,主线程不会阻塞
go 函数名(参数列表)
package main //import "time" var x int; var ed [2]int; func madd(tid int){ for i:= 0;i < 10;i++{ x++ println(x) } ed[tid] = tid } func main(){ go madd(1) go madd(2) println("ed[0]", ed[0]) println("ed[1]", ed[1]) }
只输出了
ed[0] 0
ed[1] 0
package main import "time" var x int; var ed [2]int; func madd(tid int){ for i:= 0;i < 10;i++{ x++ println(x) } ed[tid - 1] = tid } func main(){ go madd(1) go madd(2) time.Sleep(2 * time.Second)
println("ed[0]", ed[0])
println("ed[1]", ed[1])
}
则能够输出
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
19
20
ed[0] 1
ed[1] 2
b. channel 用来在goroutine之间传递数据,操作符<-用于指定通道方向
声明通道: ch:= make(chan type [,capacity])
发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。
不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。
注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。
发送: ch<-v
接受: v = <-ch
package main import "math/rand" func randGenerator(c chan float32) float32{ for i := 0; i < 10;i++{ c <- rand.Float32() } return 0 } func add(c chan float32, isEnd chan int) float32{ var sum float32 = 0.0 for i := 0; i < 10;i++{ sum += <- c println(sum) } isEnd <- 0 return 0 } func main(){ c := make(chan float32) isEnd := make(chan int) go randGenerator(c) go add(c, isEnd) endstatus := <- isEnd println(endstatus) }
可以用for + range遍历,用close访问
package main import "math/rand" func randGenerator(c chan float32) float32{ for i := 0; i < 5;i++{ c <- rand.Float32() } close(c) return 0 } func main(){ c := make(chan float32) go randGenerator(c) for rd := range c{ println(rd) } }
21. Variadic Function
Here’s a function that will take an arbitrary number of ints as arguments.
Variadic functions can be called in the usual way with individual arguments.
If you already have multiple args in a slice, apply them to a variadic function using func(slice...) like this.
package main import "fmt" func sum(nums ...int) { fmt.Print(nums, " ") total := 0 for _, num := range nums { total += num } fmt.Println(total) } func main() { sum(1, 2) sum(1, 2, 3) nums := []int{1, 2, 3, 4} sum(nums...) }