记录一下之前的Go学习笔记。
1.变量定义
示例:
1package main
2
3import "fmt" //goland会帮我们自动导入程序中使用的包
4
5func main() {
6 //变量定义: var
7 //常量定义: const
8
9 //01-先定义变量,再赋值 var 变量名 数据类型
10 var name string
11 name = "duke" //Ctrl + Alt +l 可以快速格式化代码
12
13 var age int
14 age = 20
15
16 fmt.Println("name:", name)
17 fmt.Printf("name is :%s, %d\n", name, age)
18
19 //02 定义时直接赋值
20 var gender = "man"
21 fmt.Println("gender:", gender)
22
23 //03 定义直接赋值,使用自动推导 (最常用的)
24 address := "北京"
25 fmt.Println("address:", address)
26
27 //灰色部分表示形参
28 test(10, "str")
29
30 //04-平行赋值
31 i, j := 10, 20 //同时定义两个变量
32 fmt.Println("变换前==》 i:", i, ", j:", j)
33
34 i, j = j, i
35 fmt.Println("变换后==》i:", i, ", j:", j)
36
37}
38
39func test(a int, b string) {
40 fmt.Println(a)
41 fmt.Println(b)
42}
运行结果:
1name: duke
2name is :duke, 20
3gender: man
4address: 北京
510
6str
7变换前==》 i: 10 , j: 20
8变换后==》i: 20 , j: 10
基础数据类型:
1int,int8 int16, int32, int64
2uint8 ... uint64
3
4float32
5float64
6
7true/false
2.自增语法
c语言: i++, i–, –i, ++i
go语言: i++, i–, 没有–i,++i, 自增语法必须单独一行
1package main
2
3import "fmt"
4
5func main() {
6 i := 20
7 i++
8 //++i //这个也是错的, 语义更加明确
9 //fmt.Println("i:", i++) //这是错误的,不允许和其他代码放在一起,必须单独一行
10 fmt.Println("i:", i)
11
12}
3. 指针
1package main
2
3import "fmt"
4
5func main() {
6 //go语义也有指针
7 //结构体成员调用时: c语言: ptr->name go语言: ptr.name
8 //go语言在使用指针时,会使用内部的垃圾回收机制(gc : garbage collector),开发人员不需要手动释放内存
9 //c语言不允许返回栈上的指针,go语言可以返回栈上的指针,程序会在编译的时候就确定了变量的分配位置:
10 //编译的时候,如果发现有必要的话,就将变量分配到堆上
11
12 name := "lily"
13 ptr := &name
14 fmt.Println("name:", *ptr)
15 fmt.Println("name ptr:", ptr)
16
17 //02-使用new关键字定义
18 name2Ptr := new(string)
19 *name2Ptr = "Duke"
20
21 fmt.Println("name2:", *name2Ptr)
22 fmt.Println("name2 ptr:", name2Ptr)
23
24 //可以返回栈上的指针,编译器在编译程序时,会自动判断这段代码,将city变量分配在堆上
25 res := testPtr()
26 fmt.Println("res city :", *res, ", address:", res)
27
28 //空指针,在c语言: null, c++: nullptr,go: nil
29
30 //if两端不用加()
31 //即使有一行代码,也必须使用{}
32 if res == nil {
33 fmt.Println("res 是空,nil")
34 } else {
35 fmt.Println("res 是非空")
36 }
37
38}
39
40//定义一个函数,返回一个string类型的指针, go语言返回写在参数列表后面
41func testPtr() *string {
42 city := "深圳"
43 ptr := &city
44 return ptr
45}
4. 不支持的语法汇总
- 自增–i, ++i不支持
- 不支持地址加减
- 不支持三目运算符 ? :
- 只有false才能代码逻辑假,数字0和nil不能(只有false/true才能表示逻辑真假,数字不能代表逻辑值)
1package main
2
3import "fmt"
4
5func main() {
6 //if 0 {
7 // fmt.Println("hello")
8 //}
9
10 //if nil {
11 // fmt.Println("hello")
12 //}
13
14 if true {
15 fmt.Println("hello")
16 }
17
18 //a, b := 1, 2
19 //x := a > b ? 0:-1
20}
5. 字符串string
1package main
2
3import "fmt"
4
5func main() {
6 //1- 定义
7 name := "duke"
8
9 //需要换行,原生输出字符串时,使用反引号``
10 usage := `./a.out <option>
11 -h help
12 -a xxxx`
13 //c语言使用单引号 + \来解决
14 fmt.Println("name :", name)
15 fmt.Println("usage :", usage)
16
17 //2. 长度,访问
18 //C++: name.length
19 //GO: string没有.length方法,可以使用自由函数len()进行处理
20 //len: 很常用
21 l1 := len(name)
22 fmt.Println("l1:", l1)
23
24 //不需要加()
25 for i := 0; i < len(name); i++ {
26 fmt.Printf("i: %d, v: %c\n", i, name[i])
27 }
28
29 //3-拼接
30 i, j := "hello", "world"
31 fmt.Println("i+j=", i+j)
32
33 //使用const修饰为常量,不能修改
34 const address = "beijing" //在编译期就确定了类型,是预处理,所以不需要推导
35 const test = 100
36 //address = "上海"
37 fmt.Println("address:", address)
38
39 //可以直接对比字符串,使用ASCII的值进行比较
40 if i > j {
41 ;
42 }
43}
5. 定长数组
1package main
2
3import "fmt"
4
5func main() {
6
7 //1-定义,定义一个具有10个数字的数组
8 //c语言定义: int nums[10] ={1,2,3,4}
9 //go语言定义:
10 // nums := [10]int{1,2,3,4} (常用方式)
11 // var nums = [10]int{1,2,3,4}
12 // var nums [10]int = [10]int{1,2,3,4}
13
14 nums := [10]int{1, 2, 3, 4,0,0,0,0}
15
16 //2-遍历,方式一
17 for i := 0; i < len(nums); i++ {
18 fmt.Println("i:", i, ", j:", nums[i])
19 }
20
21 //方式二: for range ===> python支持
22 //key是数组下标,value是数组的值
23 for key, value := range nums {
24 //key=0, value=1 => nums[0]
25 value += 1
26 //value全程只是一个临时变量,不断的被重新赋值,修改它不会修改原始数组
27 fmt.Println("key:", key, ", value:", value, "num:", nums[key])
28 }
29
30 //在go语言中,如果想忽略一个值,可以使用_
31 //如果两个都忽略,那么 就不能使用 := ,而应该直接使用 =
32 for _, value := range nums {
33 fmt.Println("_忽略key, value:", value)
34 }
35
36 //不定长数组定义
37 //3-使用make进行创建数组
38}
6. 不定长数组(切片、slice)
切片:slice,它的底层也是数组,可以动态改变长度
- 切片1:
1package main
2
3import "fmt"
4
5func main() {
6 //切片:slice,它的底层也是数组,可以动态改变长度
7 //定义一个切片,包含多个地名
8 //names := [10]string{"北京", "上海", "广州", "深圳"}
9 names := []string{"北京", "上海", "广州", "深圳"}
10 for i, v := range names {
11 fmt.Println("i:", i, "v:", v)
12 }
13
14 //1.追加数据
15 names1 := append(names, "海南")
16 fmt.Println("names:", names)
17 fmt.Println("names1:", names1)
18
19 fmt.Println("追加元素前,name的长度:", len(names), ",容量:", cap(names))
20 names = append(names, "海南")
21 //fmt.Println("names追加元素后赋值给自己:", names)
22 fmt.Println("追加元素后,name的长度:", len(names), ",容量:", cap(names))
23
24 names = append(names, "西藏")
25 fmt.Println("追加元素后,name的长度:", len(names), ",容量:", cap(names))
26
27 //2.对于一个切片,不仅有'长度'的概念len(),还有一个'容量'的概念cap()
28 nums := []int{}
29 for i := 0; i < 50; i++ {
30 nums = append(nums, i)
31 fmt.Println("len:", len(nums), ", cap:", cap(nums))
32 }
33}
运行结果:
1i: 0 v: 北京
2i: 1 v: 上海
3i: 2 v: 广州
4i: 3 v: 深圳
5names: [北京 上海 广州 深圳]
6names1: [北京 上海 广州 深圳 海南]
7追加元素前,name的长度: 4 ,容量: 4
8追加元素后,name的长度: 5 ,容量: 8
9追加元素后,name的长度: 6 ,容量: 8
10len: 1 , cap: 1
11len: 2 , cap: 2
12len: 3 , cap: 4
13len: 4 , cap: 4
14len: 5 , cap: 8
15len: 6 , cap: 8
16len: 7 , cap: 8
17len: 8 , cap: 8
18len: 9 , cap: 16
19len: 10 , cap: 16
20len: 11 , cap: 16
21len: 12 , cap: 16
22len: 13 , cap: 16
23len: 14 , cap: 16
24len: 15 , cap: 16
25len: 16 , cap: 16
26len: 17 , cap: 32
27len: 18 , cap: 32
28len: 19 , cap: 32
29len: 20 , cap: 32
30len: 21 , cap: 32
31len: 22 , cap: 32
32len: 23 , cap: 32
33len: 24 , cap: 32
34len: 25 , cap: 32
35len: 26 , cap: 32
36len: 27 , cap: 32
37len: 28 , cap: 32
38len: 29 , cap: 32
39len: 30 , cap: 32
40len: 31 , cap: 32
41len: 32 , cap: 32
42len: 33 , cap: 64
43len: 34 , cap: 64
44len: 35 , cap: 64
45len: 36 , cap: 64
46len: 37 , cap: 64
47len: 38 , cap: 64
48len: 39 , cap: 64
49len: 40 , cap: 64
50len: 41 , cap: 64
51len: 42 , cap: 64
52len: 43 , cap: 64
53len: 44 , cap: 64
54len: 45 , cap: 64
55len: 46 , cap: 64
56len: 47 , cap: 64
57len: 48 , cap: 64
58len: 49 , cap: 64
59len: 50 , cap: 64
60
61Process finished with the exit code 0
小结:
- 可以使用append进行追加数据
- len获取长度,cap获取容量
- 如果容量不足,再次追加数据时,会动态分配2倍空间
- ==切片2:==
1package main
2
3import "fmt"
4
5func main() {
6 names := [7]string{"北京", "上海", "广州", "深圳", "洛阳", "南京", "秦皇岛"}
7
8 //想基于names创建一个新的数组
9 names1 := [3]string{}
10 names1[0] = names[0]
11 names1[1] = names[1]
12 names1[2] = names[2]
13
14 //切片可以基于一个数组,灵活的创建新的数组
15 names2 := names[0:3] //左闭右开
16 fmt.Println("names2:", names2)
17
18 names2[2] = "Hello"
19 fmt.Println("修改names[2]之后, names2:", names2)
20 fmt.Println("修改names[2]之后, names:", names)
21
22 //1. 如果从0元素开始截取,那么冒号左边的数字可以省略
23 names3 := names[:5]
24 fmt.Println("name3:", names3)
25
26 //2. 如果截取到数组最后一个元素,那么冒号右边的数字可以省略
27 names4 := names[5:]
28 fmt.Println("name4:", names4)
29
30 //3. 如果想从左至右全部使用,那么冒号左右两边的数字都可以省略
31 names5 := names[:]
32 fmt.Println("name5:", names5)
33
34 //4. 也可以基于一个字符串进行切片截取 : 取字符串的字串 helloworld
35 sub1 := "helloworld"[5:7]
36 fmt.Println("sub1:", sub1) //'wo'
37
38 //5. 可以在创建空切片的时候,明确指定切片的容量,这样可以提高运行效率
39 //创建一个容量是20,当前长度是0的string类型切片
40 //make的时候,初始的值都是对应类型的零值 : bool ==> false, 数字==> 0, 字符串 ==> 空
41 str2 := make([]string, 10, 20) //第三个参数不是必须的,如果没填写,则默认与长度相同
42 fmt.Println("str2:", &str2[0])
43
44 fmt.Println("str2: len:", len(str2), ", cap:", cap(str2))
45 str2[0] = "hello"
46 str2[1] = "world"
47
48 //6.如果想让切片完全独立于原始数组,可以使用copy()函数来完成
49 namesCopy := make([]string, len(names))
50 //func copy(dst, src []Type) int
51 //names是一个数组,copy函数介收的参数类型是切片,所以需要使用[:]将数组变成切片
52 copy(namesCopy, names[:])
53 namesCopy[0] = "香港"
54 fmt.Println("namesCopy:", namesCopy)
55 fmt.Println("naemes本身:", names)
56
57}
运行结果:
1names2: [北京 上海 广州]
2修改names[2]之后, names2: [北京 上海 Hello]
3修改names[2]之后, names: [北京 上海 Hello 深圳 洛阳 南京 秦皇岛]
4name3: [北京 上海 Hello 深圳 洛阳]
5name4: [南京 秦皇岛]
6name5: [北京 上海 Hello 深圳 洛阳 南京 秦皇岛]
7sub1: wo
8str2: 0xc000076000
9str2: len: 10 , cap: 20
10namesCopy: [香港 上海 Hello 深圳 洛阳 南京 秦皇岛]
11naemes本身: [北京 上海 Hello 深圳 洛阳 南京 秦皇岛]
12
13Process finished with the exit code 0
7.字典(map)
哈希表 , key=>value, 存储的key是经过哈希运算的
1package main
2
3import "fmt"
4
5func main() {
6 //1. 定义一个字典
7 //学生id ==> 学生名字 idNames
8 var idNames map[int]string //定义一个map,此时这个map是不能直接赋值的,它是空的
9
10 idNames[0] = "duke"
11 idNames[1] = "lily"
12
13 for key, value := range idNames {
14 fmt.Println("key:", key, ", value:", value)
15 }
16}
1panic: assignment to entry in nil map
2
3goroutine 1 [running]:
4main.main()
5 E:/projects/GoProjects/src/main.go:10 +0x2a
6
7Process finished with the exit code 2
上面例子使用map没有分配空间,导致空指针异常。
因此,使用map之前,一定要对map进行分配空间
1package main
2
3import "fmt"
4
5func main() {
6 //1. 定义一个字典
7 //学生id ==> 学生名字 idNames
8 var idNames map[int]string //定义一个map,此时这个map是不能直接赋值的,它是空的
9
10 //2. 分配空间,使用make,可以不指定长度,但是建议直接指定长度,性能更好
11 idScore := make(map[int]float64) //这个也是正确的
12 idNames = make(map[int]string, 10) //建议使用这种方式
13
14 //3. 定义时直接分配空间
15 //idNames1 := make(map[int]string, 10) //这是最常用的方法
16
17 idNames[0] = "duke"
18 idNames[1] = "lily"
19
20 //4. 遍历map
21 for key, value := range idNames {
22 fmt.Println("key:", key, ", value:", value)
23 }
24
25 //5. 如何确定一个key是否存在map中
26 //在map中不存在访问越界的问题,它认为所有的key都是有效的,所以访问一个不存在的key不会崩溃,返回这个类型的零值
27 //零值: bool=》false, 数字=》0,字符串=》空
28 name9 := idNames[9]
29 fmt.Println("name9:", name9) //空
30 fmt.Println("idScore[100]:", idScore[100]) //0
31
32 //无法通过获取value来判断一个key是否存在,因此我们需要一个能够校验key是否存在的方式
33 value, ok := idNames[1] //如果id=1是存在的,那么value就是key=1对应值,ok返回true, 反之返回零值,false
34 if ok {
35 fmt.Println("id=1这个key是存在的,value为:", value)
36 }
37
38 value, ok = idNames[10]
39 if ok {
40 fmt.Println("id=10这个key是存在的,value为:", value)
41 } else {
42 fmt.Println("id=10这个key不存在,value为:", value)
43 }
44
45 //6. 删除map中的元素
46 //使用自由函数delete来删除指定的key
47 fmt.Println("idNames删除前:", idNames)
48 delete(idNames, 1) //"lily"被kill
49 delete(idNames, 100) //不会报错
50 fmt.Println("idNames删除后:", idNames)
51
52 //并发任务处理的时候,需要对map进行上锁 //TODO
53}
运行结果:
1key: 0 , value: duke
2key: 1 , value: lily
3name9:
4idScore[100]: 0
5id=1这个key是存在的,value为: lily
6id=10这个key不存在,value为:
7idNames删除前: map[0:duke 1:lily]
8idNames删除后: map[0:duke]
8. 函数
1package main
2
3import "fmt"
4
5//1.函数返回值在参数列表之后
6//2.如果有多个返回值,需要使用圆括号包裹,多个参数之间使用,分割
7func test2(a int, b int, c string) (int, string, bool) {
8 return a + b, c, true
9}
10
11func test3(a, b int, c string) (res int, str string, bl bool) {
12
13 var i, j, k int
14 i = 1
15 j = 2
16 k = 3
17 fmt.Println(i, j, k)
18
19 //直接使用返回值的变量名字参与运算
20 res = a + b
21 str = c
22 bl = true
23
24 //当返回值有名字的时候,可以直接简写return
25 return
26
27 //return a + b, c, true
28}
29
30//如果返回值只有一个参数,并且没有名字,那么不需要加圆括号
31func test4() int {
32 return 10
33}
34
35func main() {
36 v1, s1, _ := test2(10, 20, "hello")
37 fmt.Println("v1:", v1, ",s1:", s1)
38
39 v3, s3, _ := test3(20, 30, "world")
40 fmt.Println("v3:", v3, ", s3:", s3)
41}
内存逃逸:
1package main
2
3import "fmt"
4
5func main() {
6 p1 := testPtr1()
7 fmt.Println("*p1:", *p1)
8}
9
10//定义一个函数,返回一个string类型的指针, go语言返回写在参数列表后面
11func testPtr1() *string {
12 name := "Duke"
13 p0 := &name
14 fmt.Println("*p0:", *p0)
15
16 city := "深圳"
17 ptr := &city
18 return ptr
19}
1go build -o test.exe --gcflags "-m -m -l" 11-内存逃逸.go > 1.txt 2>&1
2
3grep -E "name|city" 1.txt --color
9. import
创建目录结构:
add.go
1package add
2
3func add(a, b int) int {
4 return a + b
5}
sub.go
1package sub
2
3//在go语言中,同一层级目录,不允许出现多个包名
4func Sub(a, b int) int {
5 test4() //由于test4与sub.go在同一个包下面,所以可以使用,并且不需要sub.形式
6 return a - b
7}
utils.go
1package sub
2
3//package utils //不允许出现多个包名
4
5import "fmt"
6
7func test4() {
8 fmt.Println("this is test4() in sub/utils!")
9}
main.go
1package main
2
3import (
4 SUB "12-import/sub" //SUB是我们自己重命名的包名
5 //"12-import/sub" //sub是文件名,同时也是包名
6 . "12-import/sub" //.代表用户在调用这个包里面的函数时,不需要使用包名.的形式,不见一使用的
7 "fmt"
8)
9
10func main() {
11 //res := sub.Sub(20, 10) //包名.函数去调用
12 res := SUB.Sub(20, 10) //包名.函数去调用
13 fmt.Println("sub.Sub(20,10) =", res)
14
15 res1 := Sub(30, 20)
16 fmt.Println("Sub(30, 20) :", res1)
17
18 //这个无法被调用,是因为函数的首写字母是小写的
19 //如果一个包里面的函数想对外提供访问权限,那么一定要首写字母大写,
20 // 大写字母开头的函数相当于 public,
21 // 小写字母开头的函数相当于 private, 只有相同包名的文件才能使用
22 //add.add(10,20)
23}
结果: