记录一下之前的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. 不支持的语法汇总

  1. 自增–i, ++i不支持
  2. 不支持地址加减
  3. 不支持三目运算符 ? :
  4. 只有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

小结:

  1. 可以使用append进行追加数据
  2. len获取长度,cap获取容量
  3. 如果容量不足,再次追加数据时,会动态分配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

1585902031122

9. import

创建目录结构:

1585903432739

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}

结果:

1585903519834