指针类型

定义指针

1
2
3
var a int = 10
var p *int = &a
pp := &a

空指针

  • 默认值,空指针的值为 nil

访问指针

  • 使用 *ptr 访问值,地址无法访问将产生运行错误。
  • 结构体使用 name.attr 访问元素,在 Golang 中可以使用 ptr.attr 通过指针访问元素(Clangptr->attr)。

结构体类型

定义结构体

  • 具名结构体
1
2
3
4
5
6
7
8
9
type Book struct {
name string
author string
}
var a Book // 定义默认结构体
var b = Book{"Golang 入门经典", "Unknown"} // 全部初始化
var c = Book{name: "C++ 入门指南"} // 局部初始化
d := Book{name: "鸟哥的 Linux 私房菜"} // 使用 :=
var e *Book = new(Book) // 使用指针
  • 字段匿名
1
2
3
4
5
type Book struct {
string
string
}
var a Book = Book{"Golang 入门经典", "Unknown"}
  • 结构体匿名
1
2
3
4
var a = struct{
name string
author string
}{"Golang 入门经典", "Unknown"}
  • 两种匿名不可同时使用。

访问结构体

1
2
3
4
var a Book = Book{"Golang 入门经典", "Unknown"}
var p *Book = &a
fmt.Println(a.name, a.author)
fmt.Println(p.name, p.author) // 结构体指针直接使用 ptr.attr 访问

类型转换

  • 如果两个结构体具有完全相同的定义,它们可以进行显式类型转换。

嵌套与继承

  • 结构体嵌套将一个结构体作为新结构体的一个字段。
1
2
3
4
type Library struct {
name string
books []Book
}
  • 结构体继承将一个结构体的字段和方法引入新结构体。结构体的方法后面会提及。
1
2
3
4
type ChildBook struct {
fitAge int
Book
}

数组类型

定义数组

1
2
3
4
var a [3]int
var b = [3]int{1, 2, 3}
var c = [...]int{1, 2, 3} // 注意 [...]int 是数组, []int 是切片 (下一节会讲)
d := [...]int{1, 2, 3}

传递数组

  • Golang 中数组名表示整个数组。这与 Clang 表示数组头指针不同,但与其 std::array 类似。传递数组有时会产生很大的开销,可以使用指针数组。
1
2
var a [3]int = [3]int{1, 2, 3}
var p *[3]int = &a

切片类型

定义切片

1
2
3
4
5
var a []int  // [], 注意 [3]int / [...]int 都是数组不是切片
var a = []int{1, 2, 3} // [1 2 3]
a := []int{1, 2, 3} // [1 2 3]
var a = make([]int, 3) // [0 0 0]
// make: make 只用于构造 slice / map / channel, 第一个参数为类型, 第二个参数为大小

切片原型

  • slice 是引用数据类型,对切片的赋值不会拷贝数据,切片的切片也是引用而非拷贝。
  • 后面介绍的所有切片操作请注意区别 引用与拷贝
1
2
3
4
5
type SliceHeader struct {
Data uintptr // 数据存储地址
Len int // 数据长度
Cap int // 数据申请长度
}

切片操作

切片

  • 切片是一种数据类型也是一种操作

  • 进行切片的对象可以是数组或切片,将返回 SliceHeader ,是对数据的引用而非拷贝。

  • Golang 中的切片应与 Python 区分的是:Golang 中索引不可使用负数表示,并且不可设置步长。

1
2
3
4
5
var a = []int{1, 2, 3}
b := a[:] // [1 2 3] 如果 a 是一个数组, 此方法将创建一个切片
c := a[1:] // [2 3]
c := a[:2] // [1 2]
c := a[1:2] // [2]

append() 与 copy()

  • append()copy() 都是对数据的拷贝,会对引用到的所有数据进行拷贝。
1
2
3
4
5
6
var a = []int{1, 2}
var b = []int{4, 5, 6}
var c []int = append(a, 4) // [1 2 4] 传入切片类型与元素类型, 返回切片类型
var d []int = append(a, 4, 5, 6) // [1 2 4 5 6] 传入切片类型与多个元素类型,返回切片类型
var e []int = append(a, b...) // [1 2 4 5 6] 传入两个切片类型, 返回切片类型, 是将 b 追加到 a 尾后的新切片
var i int = copy(a, b) // 2 [4 5] [4 5 6] 将 b 拷贝到 a , 返回拷贝量, 拷贝量为 min(len(a), len(b))
  • Golangargs... 表示解包,对应 Python 中的 *args

常见组合操作

拷贝切片
1
b := append([]int{}, a...)
1
2
b := make([]int, len(a))
copy(b, a)
原地删除
  • 原地删除首部
1
a = a[st:]  // 删除前 st 个元素
  • 原地删除尾部
1
a = a[:ed]  // 保留前 ed 个元素
  • 原地截取中部
1
a = a[st:ed]  // 截取索引 [st, ed) 元素
  • 原地删除中部
1
a = append(a[:st], a[ed:]...)  // 删除索引 [st, ed) 元素
原地插入
  • 原地末尾追加
1
a = append(a, b...)
  • 原地首部插入
1
a = append(b, a...)
  • 原地中间插入
1
a = append(append(a[:st], b...), a[st:]...)  // 从第 st 个元素开始插入元素

字符串类型

定义字符串

  • Golang 字符串默认使用 UTF-8 编码。
1
2
3
var a string = "Hello World!"
var b = "Golang 入门经典"
c := "abcABC123"

字符串原型

  • 字符串是引用数据类型,底层使用 [...]byte 存储,而使用 UTF-8 解析。与 Java 相似字符串作常量使用,不直接修改其值。
1
2
3
4
type StringHeader struct {
Data uintptr
Len int
}

字符串操作

  • 许多字符串操作位于库文件 strings 中,许多字符串类型转换位于库文件 strconv 中。
  • 这里假设已经定义了两个变量:
1
2
var a = "你好"
var b = "Hello"

字符串切片

  • 字符串可以认为是一个静态切片类型,可以进行相应的切片操作。不可使用 appendcopy 函数。

字符串拼接

1
c := a + b  // 运算符 两字符串拼接
1
c = strings.Join([]string{a, b}, "")  // 函数 多字符串拼接
1
2
3
4
5
// 官方推荐的 最高效率
var builder strings.Builder // 或 builder := strings.Builder{}
builder.WriteString(a)
builder.WriteString(b)
var s string = builder.String() // 或 s := builder.String()

字符串编码

1
2
c := []byte(a)  // 转换为 ASCII 编码, 一个 Unicode 字符占用 3 Bytes
d := len(c) // 6
1
2
c := []rune(a)  // 转换为 UTF-8 编码
d := len(c) // 2
  • string 类型直接使用 len() ,返回值为 ASCII 编码长度。

其他操作

1
2
3
4
5
func strings.Contains(s string, substr string) bool   // s 是否包含 substr
func strings.HasPrefix(s string, prefix string) bool // s 是否以 substr 开头
func strings.HasSuffix(s string, suffix string) bool // s 是否以 substr 结尾
func strings.Index(s string, substr string) int // substr 在 s 中第一次出现的索引
func strings.LastIndex(s string, substr string) int // substr 在 s 中最后次出现的索引
1
2
3
func strings.ToLower(s string) string            // 返回 s 的小写形式
func strings.ToUpper(s string) string // 返回 s 的大写形式
func strings.EqualFold(s string, t string) bool // 不区分大小写的情况下, s 与 t 是否相等
1
2
3
4
5
6
7
func strings.Split(s string, sep string) []string   // 以 sep 分割字符串 s
func strings.SplitN(s string, sep string, n int) []string
// 以 sep 分割字符串 s. 最多将字符串分割为 n 份, -1 表示无穷
func strings.SplitAfter(s string, sep string) []string // 以 sep 分割字符串 s, sep 会保留在前一个字符串尾
func strings.SplitAfterN(s string, sep string, n int) []string
// 以 sep 分割字符串 s, sep 会保留在前一个字符串尾. 最多将字符串分割为 n 份, -1 表示无穷
// ??? 为什么 go 没有 rsplit
1
func strings.Join(elem []string, sep string) string  // 以 sep 衔接 elem 中元素, Split 的逆过程
1
2
3
func strings.Replace(s string, old string, new string, n int) string  
// 将 s 中的 old 替换为 new 至多 n 次, n 为 -1 表示无穷
func strings.ReplaceAll(s string, old string, new string) string // 替换全部
1
func string.Repeat(s string, n int) string  // s 重复 n 次
1
2
3
4
5
6
7
8
func strings.TrimSpace(s string) string                  // 去除前后空字符
func strings.Trim(s string, chars string) string // 去除前后 chars 中的字符
func strings.TrimLeft(s string, chars string) string // 去除前导 chars 中的字符
func strings.TrimRight(s string, chars string) string // 去除后导 chars 中的字符
func strings.TrimPrerix(s string, prefix string) string // 去除前导 prefix 字符串至多一次
// func strings.CutPrefix(s string, prefix string) (string, bool) // 功能相似, 返回是否去除
func strings.TrimSuffix(s string, suffix string) string // 去除后导 suffix 字符串至多一次
// func strings.CutSuffix(s string, suffix string) (string, bool) // 功能相似, 返回是否去除

字符串转换

转换为字符串类型

1
2
func fmt.Sprint(a ...any) string  // 将任意类型转换为字符串, 只要类型满足 fmt.Stringer 接口
// fmt.Sprint(Complex(1, 5))

转换自字符串类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func strconv.ParseBool(s string) (bool, error)  
// 将 string 转为 bool 类型 (true/false), 如果错误存在同时返回
func strconv.ParseComplex(s string, bitSize int) (complex128, error)
// 将 string 转为 complex 类型, 如果错误存在同时返回
// bitSize 为 64/128 表示 complex64/complex128, 一般使用 128
func strconv.ParseFloat(s string, bitSize int) (float64, error)
// 将 string 转为 complex 类型, 如果错误存在同时返回
// bitSize 为 32/64 表示 float32/float64, 一般使用 64
func strconv.ParseInt(s string, base int, bitSize int) (int64, error)
// 将 string 转为 complex 类型, 如果错误存在同时返回
// base 为进制 0/2-36, 特别地, 0 表示自动进制
// 0b 开头被识别为二进制
// 0o 开头被识别为八进制
// 0x 开头被识别为十六进制
// 否则被识别为十进制
// bitSize 为 8/16/32/64 表示 int8/int16/int32/int64, 一般使用 64
func strconv.ParseUint(s string, base int, bitSize int) (uint64, error) // 与 strconv.ParseInt 相似