01.Go语言基础语法易错点总结

[TOC]

一.基础语法易错知识点

(一).数据类型

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
1.Golang是强类型语言。
2.在赋值过程中、类型必须保持一致。
3.变量必须先定义后使用,且必须用到。
4.golang会为每个变量设置默认值。
5.变量不能重名。
6.golang会根据类型做变量类型推断。
7.代码规范
- 变量一般都是有小驼峰命名法
- 如果外部引用 用大驼峰命名法
- 数据库使用大驼峰命名法
8.数据库ORM参考学习
https://github.com/go-gorm/gen

9.字符串和整型互转案例
numType := 100
fmt.Printf("Type: %T\n",strconv.Itoa(numType))
str := "1000"
ret,_ := strconv.Atoi(str)
fmt.Printf("Type:%T\n",ret)

10.如下测试改变的是盒子里面放的内容,而不是盒子。
age := "23"
fmt.Printf("getMemoryAddress: %p\nage: %s\n", &age, age)

age = "24"
fmt.Printf("getMemoryAddress: %p\nage: %s\n", &age, age)

11.变量不可以重名 但是在新的作用域内是可以同名
{
var age string
age = "jwgodAge"
fmt.Printf("我是新的作用域:%v\n",age)
}
12.常量在整个生命周期都不允许修改。
- 常量是在编译过程中计算好的值。
- 变量是在运行过程中配置的值。
- 常量只能支持: bool/string/数字。
- 变量没有限制。

13.goland中
//- alt+shift+鼠标滑动 可以复制列。
- shift+鼠标左键 可以选中多行或多列
- alt+鼠标左键 单击选中多个光标进行修改。


14.运算符中a/b 如果都是int类型 结果将会舍去小数位直接显示整数位 需要特别注意。

15.位运算案例
func bitCalc(){
arr := []int{4,-3,4,5,6,-3,5,6,7}
ret := -1
for _,v := range arr{
if ret <0 {
ret = v
fmt.Printf("ret<0: %v\n",ret)
}else {
ret = ret ^ v
fmt.Printf("ret>=0: %v\n",ret)
}
}
fmt.Printf("最终ret变量结果: %v\n",ret)
}

16.fmt有迷惑性参数汇总
%p # 表示为十六进制,并加上前导的0x
%f # 默认宽度,默认精度
%9f # 宽度9,默认精度
%.2f # 默认宽度,精度2
%9.2f # 宽度9,精度2
%9.f # 宽度9,精度0

17.课程涉及站点
http://doc.golang.ltd/
https://github.com/boltdb/bolt/
https://crontab.guru/
https://golangdocs.com/
https://blog.gopheracademy.com/
https://www.manning.com/books/the-programmers-brain
https://forge.medium.com/the-easiest-thing-you-can-do-to-make-a-boring-job-better-7166d9e9e202

18.指定平台编译方式
GOOS=windows GOARCH=amd64 go build -o bfr_x64.exe ./main.go //64bit Win
GOOS=windows GOARCH=386 go build -o bfr_x32.exe ./main.go //32bit Win
GOOS=linux GOARCH=amd64 go build -o bfr_x64_linux ./main.go //64bit Linux

19.go mod初始化方式
go mod init gocamp_home_study

20.pprof 性能分析相关
https://golang.google.cn/pkg/runtime/pprof/
https://golang.google.cn/pkg/net/http/pprof/#Index
https://github.com/google/pprof/
https://studygolang.com/articles/12970

21.keymap可以设置快捷键
22.General->Appearance->show whitespace 显示隐藏的字段。
23.NaN: not a number的简写。
24.windows->editor Tabs->split right/down // 分屏

25.双引号 单引号 反引号
- 单引号表示byte类型或rune类型,对应uint8int32类型,默认是rune类型。byte用来强调数据是raw data,而不是数字,而rune用来表示Unicode的code point。

- 双引号才是字符串,实际上是字符数组。可以用索引号访问某字节,也可以用len()函数来获取字符串所占的字节长度。

- 反引号,表示字符串字面量,但不支持任何转义序列。字面量raw literal string的意思是,你定义时写的啥样,它就啥样,你有换行,它就换行。你写转义字符,它也就展示转义字符。

//示例
func quoteTest(){
//双引号"",可转义。
x := "x\nquote\ttest"
fmt.Printf("x is:%v,type is %T\n",x,x)

//反引号``,保持原有格式输出
var codeExample string =`
func main(){
fmt.Println("Hi jwgod~")
}
`
fmt.Println(codeExample)

//单引号'',通常用来表示rune类型,展示unicode。
var a byte = 'H'
fmt.Printf("a:%v,type:%T\n",a,a)

b := '你'
fmt.Printf("b:%v,type:%T\n",b,b)
}

//结果
a:72,type:uint8
b:20320,type:int32

26.字符串是utf-8编码,一个汉字三个字节,一个字母一个字节。
//utf-8它可以使用1-4个字节表示一个字符,根据字符的不同变换长度。
//字符长度和字节长度理解
https://mp.weixin.qq.com/s/pXO4LxehqENEvRvxCXhG4A

27.Goland todo关键字
//fixme 待修复bug
//todo 待做事项

28.generate->Implement Methods
- 自动创建未实现的方法。

29.选中字段名->refactor->rename // 批量修改

30.alt+shift+command+F 代码快速格式化
31.Mac下常用键盘符对应关系
⌘(command)
⌥(option)
⇧(shift)
⇪(caps lock)
⌃(control)
↩(return
⌅(enter)
如果不记得了 可以选择Mac下右上角的输入法图标,选择虚拟键盘即可看到对应关系。

(二).位操作符

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
1.与操作:&
1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0

#(The result of 5&6 is ', 4)
5 二进制为 101
6 二进制为 110
按照&的计算规则 相同为1 相异为0 5&6的结果二进制100 二进制100对应上面的4。

2.或操作:|
1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0

('The result of 5|6 is ', 7)
5 二进制为 101
6 二进制为 110
按照|的计算规则 只要有一个为1 结果就为1,5|6的结果二进制111 二进制111对应上面的7。

3.异或:^ 同0异1
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0

('The result of 5^6 is ', 3)
5 二进制为 101
6 二进制为 110
按照|的计算规则 上下对应相异 结果就为1 5^6的结果二进制011 二进制011对应上面的3 正常情况会忽略前面的0。

4.左移:<<
1 << 2 = 4
('The result of 1<<2 is ', 4) #1左移2位
0000 0001 #1的二进制如左边 所有位数左移2位
0000 0100 #左移2位结果如左边 对应十进制的4

5.右移:>>
8 >> 2 = 2

('The result of 8>>2 is ', 2) #8右移2位案例
0000 1000 #8的二进制。
0000 0010 #所有右移2位结果 对应十进制2的二进制。

(二).源码/反码/补码

1

(三).array

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
1.自动计算数组的长度,不用再刻意定义。
arr := [...]int{1,2,3,4,5}
fmt.Println(arr)

2.数组赋值
//如果没有赋值 默认结果为 a = [3]int{0,0,0}
a := [3]int{}
a[0]=0
a[1]=0
a[2]=0

3.数组和多维数组核心知识点测试代码
//测试定长数组
func arrayExample1(x [5]int){
//原始值
fmt.Println(x)

//for循环打印结果方式
for i:=0;i<len(x);i++{
fmt.Printf("for: x[%d]=%d\n",i,x[i])
}

//for range 遍历数组打印结果方式
for k,v := range x{
fmt.Printf("range: x[%d]=%d\n",k,v)
}
}

//测试变长 切片实现
func arrayExample2(array2 []int){

for i:=0;i<len(array2);i++{
array2[i] = i+99
}
fmt.Println(array2)

//测试数组长度自动计算获取
array3 := [...]int{1,1,1,1,1}
fmt.Println(array3)
}


//测试多维数组
func arrarExample3(){
var arr3 [3][3]int
arr3 = [3][3]int{{1,2,4},{2,4,6},{23,23,23}}
fmt.Println(arr3)

//数组设置为变长
var arr4 = [...][3]string{
[3]string{"jwgod","23","engineer1"},
[3]string{"jwgod1","24","engineer2"},
[3]string{"jwgod2","25","engineer3"},
[3]string{"jwgod3","26","engineer4"},
[3]string{"jwgod4","27","engineer5"},
[3]string{"jwgod5","28","engineer6"},
[3]string{"jwgod6","29","engineer7"},
}

for _,v := range arr4{
fmt.Println(v)
}

//多重遍历取最终值
for x,y :=range arr4{
for x1,y2 := range y{
fmt.Printf("arr4[%d][%d]=%v\n",x,x1,y2)
}

}
}

(四).slice

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
1.slice中一些比较关键的操作
- 第一种
make([]Type ,length, capacity)
slice := make([]int, 3, 5)

- 第二种
make([]Type, length)
slice := make([]int, 5)

- 第三种
[]Type{}
slice := []int{}
- 第四种
slice := []int{1, 2, 3, 4, 5} //len是根据初始化长度而定的

- 第五种
//append内部实现了slice的自增,内部进行了make初始化。
//append无论如何都是从slice的尾部开始追加数据,每次append 都要重新分配一次内存。
func sliceStudy() {
//追加元素
a := []int{11, 22, 33}
b := []int{1, 2, 3, 4, 4, 5}
//如果append的数据为切片 别忘记...
a = append(a, b...)
fmt.Println("将b切片追加到a切片中。")
fmt.Println(a)

//删除元素4 ...表示追加多个元素
c := []int{1, 2, 3, 4, 5, 6, 7, 8}
c = append(c[:3], c[4:]...)
fmt.Println(c)

//删除多个元素 3 7 8
d := []int{1, 2, 3, 4, 5, 6, 7, 8}
d = append(d[:3], d[4:6]...)
fmt.Println(d)
}

//copy以最小的为基准
func copySlice(){
x := make([]int,4) //dst
y := []int{1,2,3} //src
copy(x,y)
fmt.Println(x)
}

(五).map

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
30
31
32
33
1.map注意事项
- 不用实例化就可以读取 删除。
- 必须实例化才可以写入。
- 重复删除相同的key,不会引起异常。

2.map初始化和案例
- 第一种初始化方式
var numbers map[string]int
numbers = make(map[string]int)

- 第二种声明并初始化
number := make(map[string]int)

- 案例
func mapExample1() {
m4 := map[string]int{"name": 1, "sex": 2, "addr": 3}
m4["name"] = 111
fmt.Println("m4:modify", m4)

delete(m4, "sex")
fmt.Println("m4:delete", m4)

_, ok := m4["addr"]
if ok {
fmt.Println(m4["addr"])
}

//遍历map
for k,v := range m4{
fmt.Printf("k=%v,v=%v\n",k,v)
}

}