Go 学习笔记

The Go Programming Language

与其它语言的对比

Go C/C++ Java Python
内存管理 GC 手动管理 GC GC
有无标准格式 ✔️
操作符重载 ✔️ ✔️
参数默认值 ✔️ ✔️
构造函数与析构函数 ✔️ 仅构造函数 ✔️
继承 ✔️ ✔️ ✔️
模板 ✔️ ✔️
异常 ✔️ ✔️ ✔️
✔️
隐式数值类型转换 ✔️ ✔️
注解 ✔️

Go 官方工具链

工具 go - go run source-code.go - 直接运行Go程序 - go build source-code.go - 编译Go源代码

代码格式化工具 gofmt

Go 语法

package 包

类似于 Java 的 package,但不要求放在对应的目录中。一个包可以由多个源代码文件组成。

常用的包: - os - 操作系统无关的函数及变量(类似 Python 的 os) - fmt - 输入输出(类似 C++ 的iostream

导入包的语法

import fmt - 导入单个包 fmt

1
2
3
4
import (
"fmt"
"os"
)
导入两个包 fmtos

编写语句

⚠️ 注意事项

  • 和 Python 一样,每行一般无需分号,但是 如果一行内有多个语句则要加分号
  • 大括号必须紧跟函数声明或 for 循环之后,不能换行
  • 换行只能在操作符之后换行,不能在操作符前换行

变量声明

1
var variable_name int

声明一个变量格式如上,首先用关键字 var 说明要声明的是变量,随后跟上变量名,最后指明其数据类型。

以该方式声明的变量会被赋初值,int 类型的会被赋值 0,bool 类型赋值 falsestring 类型则赋值 ""

Go 支持自动类型推导,故以下两个语句是等价的

1
var s string = "ABCD" // 显式声明变量的类型
1
var s = "ABCD" // 自动类型推导

也可以同时声明/初始化多个变量:

1
2
3
4
5
var i, j, k int // int, int, int
var b, f, s = true, 2.3, "four" // bool, float64, string

// 使用多返回值的函数来初始化多变量
var f, err = os.Open(name) // os.Open 函数返回 file 和 error

还有一种变量声明的方法,被称为短变量声明 (short variable declaration),可以看作是一种语法糖,不需要添加关键字 var,而是使用一个新的符号 :=需要注意的是,该种形式只能在函数内部充当局部变量,不能作为全局变量。

1
s := "ABCD"

需要注意的是,:= 是声明,而 = 是赋值。因此使用 := 时,必须至少有一个变量是语法块中新的变量;如果是一个已经被声明的变量,则其行为和赋值操作一样。

操作符

自增、自减操作符:++--只能在变量之后使用,不能前缀使用,也就是 c++ 是合法的,但是 ++c 非法;使用这两种操作符时,语句仅仅只是个语句,而不是表达式,不能得到值,也就是 C/C++ 中的 j = i++ 在 Go 中是非法的。

循环

Go 中循环只有一个,使用 for 关键字来定义,语法如下:

1
2
3
for initialization; condition; post {
// 零个或多个语句
}
与 C/C++ 相同的是,三个语句皆可省略,但是省略了之后分号也可以省略:
1
2
3
4
5
6
7
8
9
// 省略 initialization 和 post
for condition {
// 实现 C/C++ 中 while 循环同样的效果
}

// 省略所有
for {
// 无限循环,实现 while(true) 的效果
}
Go 也可以使用类似 Python 中的 for range 循环:
1
2
3
4
s := ""
for _, arg := range os.Args[1:] {
s += " " + arg
}
其中 range 会在每次循环开始之时返回两个对象,一个索引 (index),一个则是索引对应的值。若要弃用某一个值,则用 _ 代替。

指针

和 C/C++ 一样,可以对某个对象使用取地址操作符 &,对指针使用 * 操作符可以获取其指向的对象:

1
2
3
4
5
// 返回一个 int 指针
func function() *int {
v := 10
return &v // 在 C/C++ 中这么做会导致“野指针”问题,返回栈对象的指针的结果是随机的
}
任何指针的默认值是 nil,相当于 C++ 中的 nullptr。由于 Go 不存在未被初始化的变量之说,故只声明一个指针而不赋值只会得到一个空指针。

Go 也提供了直接创建指针的机制,即使用 new(T) 函数。但是需要注意的是,其行为和对局部变量取地址是等价的,也就是下面两段代码等价:

1
2
3
func newInt() *int {
return new(int)
}
1
2
3
4
func newInt() *int {
var dummy int
return &dummy
}

内建数据类型

切片 slice

类似于数组,但是大小可以变化

创建方式:

1
2
3
4
5
6
// 创建一个初始元素长度为5的切片,元素初始值为0: 
mySlice1 := make([]int, 5)
// 创建一个初始元素长度为5的切片,元素初始值为0,并预留10个元素的存储空间:
mySlice2 := make([]int, 5, 10)
// 切片字面量创建长度为5容量为5的切片,需要注意的是 [ ] 里面不要写数组的容量,因为如果写了个数以后就是数组了,而不是切片了。
mySlice3 := []int{10,20,30,40,50}

映射 map

存储一系列的键值对,类似于 C++ 中的 std::unordered_map

创建方式:

1
2
3
4
5
6
7
8
// 创建了一个键类型为string、值类型为PersonInfo
myMap = make(map[string] PersonInfo)
// 也可以选择是否在创建时指定该map的初始存储能力,创建了一个初始存储能力为100的map
myMap = make(map[string] PersonInfo, 100)
// 创建并初始化map的代码
myMap = map[string] PersonInfo {
"1234": PersonInfo{"1", "Jack", "Room 101,..."},
}

遍历:

1
2
3
4
// map 的遍历是随机的,没有指定的顺序
for key, value := range myMap {
// myMap 为 map 对象
}
在作为参数传递时,是 按引用传递 的,因此在某一函数中对其作修改,也会影响到其它函数。

生命周期

Go 的 GC 回收策略

编译的诸限制

  • 如果存在未被引用的包,则不能通过编译

Go 学习笔记
https://devexzh.github.io/2023/Go_Manuscript/
作者
Ryker Zhu
发布于
2023年1月15日
许可协议