本文旨在最短的时间内了解Go语言,适用于有其他语言基础,想迅速入门的同学。
1. 下载安装
到Go官网:https://golang.org/下载安装包:
或者Mac的同学可以直接使用brew
命令来安装:
1 | brew update |
创建一个workspace folder
,通常放在$Home
目录下
1 | mkdir $HOME/<your_go_workspace_folder_name> |
添加环境变量:
编辑~/.zshrc
,添加如下内容:
1 | export GOPATH=$HOME/<your_go_workspace_folder_name> |
配置VSCode 支持Go
Command+ Shift + P
后,输入 Go:Install/Update Tools
,弹出的全选安装
这里需要注意如果你使用Mac的话,可能会弹出Permission denined
错误,
原因是不存在/usr/local/pkg
目录,或者这个目录权限不足。执行下面命令修复:
1 | sudo mkdir /usr/local/pkg |
Hello World
新建一个hello.go
文件,内容如下:
1 | // 定义包名。必须在源文件中非注释的第一行指明这个文件属于哪个包。 |
1 | $ go run hello.go |
可以使用go build hello.go
来生成可执行的二进制文件。
1 | $ go build hello.go |
语法
变量
Go语言使用var identifier type
的方式定义变量。
可以一次声明多个变量:
1 | var identifier1, identifier2 type |
1 | var a string = "Geek Go" |
可以使用:=
声明同时赋值变量
1 | int_val := 100 // 相当于var int_val int; int_val = 1 |
也可以多变量声明
1 | var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断 |
常量
1 | const identifier [type] = value |
数组
1 | var variable_name [SIZE] type |
例如:
1 | var balance [10] float32 |
初始化
1 | var balance = [5]int{1,2,3,4,5} |
for循环
Go语言的For循环有3种形式,
1 | // 等价于C语言中的for |
1 | // 等价于C语言中的while |
1 | // 等价于C语言中的for(;;) |
栗子:
1 | var balance = [5]int{1, 2, 3, 4, 5} |
指针
指针使用流程:
- 定义指针变量。
- 为指针变量赋值。
- 访问指针变量中指向地址的值。
1 | var var_name *var_type |
栗子:
1 | var ip *int /* 指向整型*/ |
当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
结构体
结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:
1 | type struct_variable_type struct { |
一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:
1 | variable_name := structure_variable_type {value1, value2...valuen} |
栗子:
1 | type Books struct { |
结构体也可以作为函数参数或者结构体指针来使用
1 | func printBook( book *Books ) { |
切片(Slice)
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
可以声明一个未指定大小的数组来定义切片:
1 | var identifier []type |
或使用 make() 函数来创建切片:
1 | var slice1 []type = make([]type, len) |
len为数组的长度,并且也是切片的初始长度,可以使用可选参数capacity来指定容量
类似Python列表的切片操作
1 | s := []int{0, 1, 2, 3, 4, 5, 6, 7} |
可以使用append
和copy
方法来拷贝和追加元素
make和new 的区别
- make和new都是golang用来分配内存的內建函数,且在堆上分配内存,make 即分配内存,也初始化内存。new只是将内存清零,并没有初始化内存。
- make返回的还是引用类型本身;而new返回的是指向类型的指针。
- make只能用来分配及初始化类型为slice,map,channel的数据;new可以分配任意类型的数据。
范围(Range)
在数组上使用range将传入index和值两个变量,可使用空白符”_”省略.
1 | nums := []int{1, 2, 3, 4} |
range也可以用在map的键值对上。
1 | cars := map[string]string{"A": "Audi", "B": "BMW", "T": "Tesla"} |
输出结果:
1 | T -> Tesla |
range也可以用来枚举Unicode字符串。
- 第一个参数是字符的索引,
- 第二个是字符(Unicode的值)本身。
1 | for i, c := range "ABCabc" { |
输出:
1 | 0 65 |
集合(Map)
Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
定义 Map
可以使用内建函数 make 也可以使用 map 关键字来定义 Map:
1 | /* 声明变量,默认 map 是 nil */ |
栗子:
1 | var bookAuthorMap map[string]string // 创建集合 |
打印结果:
1 | 三国演义 作者是: 罗贯中 |
查看元素是否在集合中存在:
1 | jpm, ok := bookAuthorMap["金瓶梅"] |
delete()函数用于删除集合的元素,参数为map和其对应的key:
1 | delete(bookAuthorMap, "水浒传") // 删除后集合中只剩三本了 |
递归函数
1 | func recursion() { |
类型转换
1 | type_name(expression) |
例:
1 | var sum int = 17 |
接口
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
1 |
|
错误处理
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
error类型是一个接口类型,这是它的定义:
1 | type error interface { |
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:
并发
Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。
goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
goroutine 语法格式:
1 | go 函数名( 参数列表 ) |
如:
1 | go f(x, y, z) |
例子:
1 | func concurrent_test(s string) { |
结果:
1 | Audi |
通道(channel)
通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
1 | ch <- v // 把 v 发送到通道 ch |
声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:
1 | ch := make(chan int, [buffer]) |
make 的第二个参数可以指定缓冲区的大小:
例子:
1 | func sum(s []int, c chan int) { |
下面的代码分配了三个缓冲区,但是同时使用4个造成了deadlock
1 | c1 := make(chan int, 3) |
可以通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下:
1 | v, ok := <-ch |
如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。
1 | func fibonacci(n int, c chan int) { |
Go常用数据结构和算法
Queue
1 | // initialization |