Funky's NoteBook

Go Core Dev Base

字数统计: 2,297阅读时长: 11 min
2019/01/15 Share

基础知识

常量

预声明标识符 iota 用在常量声明中,其初始值为 0。一组多个常量同时声明时其值逐行增加,iota 可以看做是自增枚举变量,专门用来初始化常量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 类似枚举的 iota
const (
c0 = iota // 0
c1 = iota // 1
c3 = iota // 2
)
//简写
const (
c0 = iota // 0
c1 // 1
c2 // 2
)
//注意 iota 逐行增加
const (
a == 1 << iota // 1
b == 1 << iota // 2
c == 1 << iota // 3
)
//算数运算
const (
u = iota * 42 // 0 (untyped integer constant)
v = iota * 42 // 42.0 (float64 constant)
w = iota * 42 // 84 (untyped integer constant)
)
// 分开的 const 语句,iota 没次都从 0 开始
const x = iota // 0
const y = iota // 0

布尔类型

1
2
3
4
5
6
7
8
9
10
11
// 赋值
ok := false
// bool 类型数据不能和整型数据转换
var a bool
a = 1 // error 1 是整型变量
//循环控制
for ; true;{ //等价于 C 语言 while(1)
...
}
// 声明 bool 变量不赋值则默认为false
var b bool // b false

整型

byte 是 uint8 的别名,不同类型的整型必须进行强制类型转换。

1
2
3
4
5
var a int = 1
var b int32 = 2
b = a //error
var a int = (1+2)*3 // 9
var b int = 1000 >> 2 // 250

复数

复数 complex64 其实是由两个 float32 浮点数表示,一个为实部、一个为虚部,

1
2
3
4
5
var value1 complex64 = 3.1 +5i
value2 := 3.1 +5i
var v = complex(2.1,3)
a = real(v)
b = image(v)

字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var a = "hello,world"
// 通过索引访问其字节单元
b := a[0]
// 但是不能修改某个字节的值
a[1] = 'a' // error
// 字符串转换成 []byte(a) 慎用
// 当数据量较大时,每转换一次都需要复制重复内容
b := []byte(a)
// 字符串尾部不包含 NULL 字符
// 基于字符串创建的切片和源字符串指向相同的数组
// 但是一样不可以修改
c := a[0:4]
d := a[1:]
// 字符串和切片的转换:字符串可以转换为字节数组,也可以转换为Unicode的字数组
e := []byte(a)
f := []rune(a)
// 拼接
g = a + b
// 字符串长度获取
len(a)
// 遍历字节数组
for i:=0;i< len(d);i++{
fmt.Println(d[i])
}
// 遍历 rune 数组
// rune 为 int32 的别名
for i, v:= range d{
fmt.Println(i,v)
}

数组

  • 数组创建
1
2
3
4
5
var arr [2]int // 一般很少使用
a := [3]int{1, 2, 3} // 指定长度
b := [...]int{1, 2, 3} // 不指定长度
c := [3]int{1:1, 2:3} //指定长度,通过索引初始化,没有被索引的元素设置默认值
d := [...]int{1:1, 2:3} //不指定长度通过索引初始化,数组长度由最后一个初始化索引值确定

相关操作:

  • 元素访问
1
2
3
4
5
a := [...]int{1, 2, 3}
b := a[0]
for i,v := range a {
...
}
  • 数组长度
1
2
3
4
5
a := [...]int{1, 2, 3}
alength := len(a)
for i := 0; i < alength ; i++ {
...
}

切片

  • 由数组创建
1
2
3
4
var a = [...]int{0, 1, 2, 3, 4, 5, 6 }
s1 := a[0:4] // [0 1 2 3]
s2 := b[:4] // [0 1 2 3]
s3 := c[2:] // [2 3 4 5 6]
  • make 创建切片

由 make 创建的切片各个元素被默认初始化成切片元素类型的零值。

1
2
3
4
// len = 10 cap = 10 cap代指容量
a := make([]int, 10) // [0 0 0 0 0 0 0 0 0 0]
// len = 10 cap = 15
b := make ([]int,10,15) // [0 0 0 0 0 0 0 0 0 0]
  • 切片支持的操作
    • len() 返回切片长度
    • cap() 返回切片底层数组容量
    • append() 对切片追加元素
    • copy() 复制切片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
a := [...]int {1, 2, 3, 4, 5, 6}
b := make([]int, 2, 4) // [0 0]
c := a[0:3] // [1, 2, 3]
len(b) // 2
cap(b) // 4

b = append(b, 1) // [0 0 1]
len(b) // 3
cap(b) // 4

b = append(b, c...) // [0 0 1]+ [1, 2, 3] = [0 0 1 1 2 3]
len(b) // 6
cap(b) // 8 **底层数组发生扩展 原有cap(b)=4 扩展长度时,如果新的长度小于容量,不会更换底层数组,否则,go 会新申请一个底层数组,拷贝这边的值过去,把原来的数组丢掉。也就是说,容量的用途是:在数据拷贝和内存申请的消耗与内存占用之间提供一个权衡。

d := make([]int, 2, 3) // [0 0]
copy(d, c) // [0 1] copy 只会复制 d 和 c 中长度最小的
len(d) // 2
cap(d) // 3
  • 切片转换
1
2
3
str := "hello,world"
a := []byte(str) // 转换为[]byte类型切片
b := []rune(str) // 转换为[]rune类型切片

字典(map)

定义类型: map[k]T

其中 k 为索引类型,可以是任意可以进行比较的类型;T是值类型

  • map 创建
1
ma := map[string]int{"a": 1, "b": 2}
  • 使用内置 make 函数创建
1
2
mp1 := make(map[int]string, 10)
mp1[1] = "tom"
  • map 支持的操作
1
2
3
4
5
6
7
8
9
10
11
mp := make(map[int]string)
mp[1] = "tom"
mp[1] = "pony"
mp[2] = "jaky"
mp[3] = "andes"
// 删除内容
delete(mp, 3)
// 遍历
for k,v := range mp{
fmt.Println("key=", k, "value=", v)
}

注意:

  • Go 内置的 map 不是并发安全的,并发安全的 map 可以使用标准包 sync 中的 map。

  • 不要直接修改 map value 内某个元素的值,如果想修改 map 的某个键值,则必须整体赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
type User struct {
name string
age int
}
ma := make(map[int]User)
andes := User {
name: "anedes",
age: 18,
}
ma[1] = andes
// ma[1].age = 19 //Error 不能通过 map 引用直接修改
andes.age = 19
ma[1] = andes // 必须整体替换

Struct

  • struct 类型字面量声明:
1
2
3
struct {
FeildName FeildType
}
  • 自定义 struct 类型声明:
1
2
3
type TypeName struct {
FeildName FeildType
}
  • struct 类型变量初始化:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type Person struct {
Name string
Age int
}

type Student struct {
*Person
Number int
}

// 不推荐该初始化方式,一旦struct增加字段,整个初始化语句会报错
a := Person{"Tom",21}

// 推荐使用Feild名字初始化方式,没有指定字段则默认初始化为类型的零值
p := &Person {
Name: "tata",
Age: 12,
}
s : Student {
Person: p,
Number: 110,
}

if 语句最佳实践

代码改写前:

1
2
3
4
5
6
if err, file := os.Open("xxx"); err == nil {
defer file.Close
// do something
} else {
return nil, err
}

改写后:

1
2
3
4
5
6
err, file := os.Open("xxxx")
if err != nil {
return nil, err
}
defer file.Close()
//do something

Switch 语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
switch i := "y"; i {
case "y", "Y":
fmt.Println("yes")
fallthrough // fallthrough 会跳过接下来的 case 条件表达式
// 直接执行下一个 case 语句
case "n", "N":
fmt.Println("no")
}

score := 85
grade := ' '

switch {
case score >= 90:
grade = 'A'
case score >= 80:
grade = 'B'
case score >= 70:
grade = 'C'
case score >= 60:
grade = 'D'
default:
grade = 'F'
}

fmt.Printf("grade=%c\n", grade) // grade=B

for 语句

  • 类似 C 的 for:
1
2
3
for init; condition; post {
...
}
  • 类似 C 的 while:
1
2
3
for condition {
...
}
  • 类似 C 的死循环语句:
1
2
3
for {
...
}

for 的其他用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 访问 map
for key, value := range map {}
for key := range map {}

// 访问 数组
for index, value := range arry{}
for index := range arry{}
for _,value := range arry{}

// 访问 切片
for index, value := range slice{}
for index := range slice{}
for _,value := range slice{}

// 访问 通道
for value := range channel{}

标签和跳转

标签

标签用于 goto、break、continue 语句的跳转,标签的语法是:

1
Label:Statement

goto

1
goto Label
  • goto 语句只能在函数内部跳转
  • goto 不能跳过内部变量声明语句,这些变量在 goto 语句的标签语句处又是可见的。
1
2
3
	goto L // BAD, 跳过 v:=3 这条语句是不允许的
v:=3
L:
  • goto 语句只能跳到同级作用域或者上层作用域内,不能跳到内部作用域内。例如:
1
2
3
4
5
6
7
8
9
10
if n%2 == 1 {
go L1
}
for n > 0 {
f()
n--
L1:
f()
n--
}

break

break 用于函数内 for、switch、select语句的执行,有2种使用格式:

  • 单独使用用于跳出break当前所在的语句
  • 和标签一起使用,用于跳出标签所标识的 for、switch、select语句的执行,可用于跳出多重循环,但标签和 break必须在同一个函数内。
1
2
3
4
5
6
7
8
9
10
11
12
13
L1:
for i := 0; ; i++ {
for j := 0; ; j++ {
if i >= 5 {
// 跳出 L1 标签所在的 for 循环
break L1
}
if j > 10 {
// 默认仅跳出离 break 最近的内层循环
break
}
}
}

continue

用于跳出 for 循环本次迭代,跳到 for 循环的下一次迭代的 post 语句处执行,也有两种使用格式

  • 单独使用,用于跳出 continue 当前所在 for

  • 和标签一起使用,用于跳出标签所标识的 for 语句的本次迭代,单标签和 continue 必须在同一个函数内。

1
2
3
4
5
6
7
8
9
10
11
12
13
L1:
for i := 0; ; i++ {
for j := 0; ; j++ {
if i >= 5 {
// 跳到 L1 标签所在的 for 循环 i++ 处执行
continue L1
}
if j > 10 {
// 默认仅跳到离 break 最近的内层循环 j++ 处执行
continue
}
}
}
CATALOG
  1. 1. 基础知识
    1. 1.1. 常量
    2. 1.2. 布尔类型
    3. 1.3. 整型
    4. 1.4. 复数
    5. 1.5. 字符串
    6. 1.6. 数组
    7. 1.7. 切片
    8. 1.8. 字典(map)
    9. 1.9. Struct
    10. 1.10. if 语句最佳实践
    11. 1.11. Switch 语句
    12. 1.12. for 语句
    13. 1.13. 标签和跳转
      1. 1.13.1. 标签
      2. 1.13.2. goto
      3. 1.13.3. break
      4. 1.13.4. continue