这是我参与「第五届青训营」伴学笔记创作活动的第 1 天
Go 语言基础语法特点及感想 | 青训营笔记
之前也写过一些别的语言,因此就写一些 Go 语言语法里个人感觉比较有特点的地方吧。
基础语法
变量和常量
声明变量有两种方法
- 使用
var声明:
var a int = 1 //声明 a 为 int 类型并赋值为 1
类型可推断的情况下可以不写出变量类型
使用 := 声明并赋值:
a := 1 //声明 a 并赋值为 1
常量可自动推断变量类型:
const a := 1
但这不代表其是可变类型,若不同语句推断出的类型有冲突,还是会报错的
条件判断
if 判断的表达式不用加小括号,必须在同一行写大括号,然后换行写语句:
if 9%2 == 0 {
fmt.Println("9 is even")
}
else {
fmt.Println("9 is odd")
}
循环
只有 for 语句,没有 while,不过 for 有多种用法:
- 经典用法:
for i := 1; i < 10; i++ {
fmt.Println(i)
}
- 类似
while的用法:
for i <= 10 {
fmt.Println(i)
i += 2
}
- 类似
while(true)的用法:
for {
fmt.Println("Loop")
}
同样的,没有小括号
switch
C++里,写 switch 最折磨的就是每次 case 之后都需要使用 break,因为不需要 break 的情况实在是远少于需要的情况。
也许正是因此,Go 语言的 switch 是默认自带 break 的,不需要每次都写 break 了,如果真有不 break 的需求,也可以通过在 case 语句的最后加上 fallthrough 来使之能执行到后面的语句。大概是刚好相反了。
同时,使用上也有少许区别,如下:
switch val1 {
case val2:
...
case val3:
...
default:
...
}
值 val1 不需要括号括起来,val2 val3 不一定必须是常量,也可以是变量,甚至可以是表达式,但必须与 val1 的类型一致,或结果一致。有一种用法,就是省略 val1 而在 val2 val3 写表达式,不过这种写法其实也就是把 val1 的值默认为 true 然后省略了而已。没错,val1 也可以为常量。
数组
同样也是长度固定的,不过声明方法有所不同,中括号放在类型前面:
var a [7]int
切片
可以认为是可变长度的数组,可以使用 make 生成并指定一个初始长度:
s := make([]string, 7)
也可以用类似生成数组的方法生成:
s := []string{"S", "a", "t", "o", "r", "i"}
同时,由于使用函数伸缩切片长度时可能会更改原指针的位置,所以必须要接收函数的返回值作为新的切片指针。
访问切片可以像 Python 一样使用 s[a:b] 的方法,但是与 Python 不同的是不能使用负值。
值得注意的是,切片的变量类型是 []Type,与数组的区别大概就是中括号里有没有填入数字吧。
map
有些类似于 C++ 的 map,不同的是其内部是无序的,既不是按键值排序,也不是按插入顺序排序。
其可以用 make 生成,也可以使用 map[Type]Type{} 生成并初始化。声明 map 的这个写法我其实挺喜欢的,比较直观。
range
主要用于遍历,和 for 结合起来使用大概类似于 foreach 的感觉:
for i, num := range nums {
fmt.Println(i, num)
}
同样,range 后不需要使用小括号,看起来 Go 似乎似乎很少在语法上用到小括号的样子(?
函数
func add(a int, b int) (int, error) {
return a + b, nil
}
函数要以关键字 func 开头,同时返回值放到后面。值得注意的是,Go 原生支持返回多个值,而一般编写时,往往函数会返回两个值,第一个值是函数的结果,第二个值用于判断函数是否正常运行。
值得注意的是,在 Go 中,函数并不支持重载。
指针
类似 C,但用途有限,可用于需要修改修改函数传递的参数时。
结构体
type user struct {
name string
password string
}
以 type 开头,不过声明结构体主要是在 struct 以及后面,type 其实发挥着将声明的 struct 命名为 user 的作用(可以这么理解)。有意思的是,Go 似乎会强制结构体内变量的类型声明对齐,这样确实整洁不少。
结构体方法
func (u user) check(password string) bool {
return u.password == password
}
跟声明函数类似,不过在 func 与函数名之间添加了结构体的的信息,这种分离结构体方法和属性的操作我觉得还是挺不错的。
不错的是,如果把 (u user) 改为 (u *user),那么在使用时就会自动将结构体指针传入,也就可以更改结构体的值了,这点还是比较智能的。
错误处理
之前说过,函数值一般会返回两个值:
val, err := function(a)
if err != nil {
fmt.Println(err)
return
}
所以一般就用如上语句判断是否有错误产生,只要 err 为 nil,一切都会好起来的。由于基本上每执行个函数都需要进行这样的判断,所以到后期,这种重复的判断错误的语句数量其实是非常可观的。
字符串操作
我的评价是,看文档。
字符串格式化
三个用法
fmt.Printf("值:%v 较详细的值:%+v 很详细的值:%#v",val,val,val)
使用 %v 可以不用管传入的是什么值,当然若是想用 %d %f 也可以
JSON 处理
可以结构体和 JSON 互转,不过结构体内的每个公开字段的首字母需要为大写。不过也可以用在字段后跟 `json:"xxx"` 的 tag 使之在 JSON 中用其他写法
时间处理
谨记 2006-01-02 15:04:05
数字解析
具体函数在 strconv 中,我的评价还是看文档。
进程信息
fmt.Println(os.Getenv["PATH"])
os.Setenv("name", "Satori")
exec.Command("command","arg1","arg2")
具体如上,感觉还是挺简单方便的
实战案例
猜谜游戏
主要是学习基本的输入输出以及判断
抓包
不得不说,看完只有感叹: Go 进行网络操作确实方便啊。
也通过讲解知道了两个不错的工具:curlconverter、JSON转Golang Struct
SOCKS5 代理
没想到这么快就快进到感觉很高级的功能了,不过侧面也说明 Go 语言进行网络操作是很方便的。
主要是需要进行鉴权 auth 和 连接 connect 的操作,然后就是对报文的处理也比较需要耐心。