Go 语言基础语法特点及感想 | 青训营笔记

160 阅读6分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 1 天

Go 语言基础语法特点及感想 | 青训营笔记

之前也写过一些别的语言,因此就写一些 Go 语言语法里个人感觉比较有特点的地方吧。

基础语法

变量和常量

声明变量有两种方法

  1. 使用 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 有多种用法:

  1. 经典用法:
for i := 1; i < 10; i++ {
    fmt.Println(i)
}
  1. 类似 while 的用法:
for i <= 10 {
    fmt.Println(i)
    i += 2
}
  1. 类似 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
} 

所以一般就用如上语句判断是否有错误产生,只要 errnil,一切都会好起来的。由于基本上每执行个函数都需要进行这样的判断,所以到后期,这种重复的判断错误的语句数量其实是非常可观的。

字符串操作

我的评价是,看文档。

字符串格式化

三个用法

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 进行网络操作确实方便啊。

也通过讲解知道了两个不错的工具:curlconverterJSON转Golang Struct

SOCKS5 代理

没想到这么快就快进到感觉很高级的功能了,不过侧面也说明 Go 语言进行网络操作是很方便的。

主要是需要进行鉴权 auth 和 连接 connect 的操作,然后就是对报文的处理也比较需要耐心。