前言
本文主要介绍Go语言的基础语法,除了上课内容外,还包括我看的其他资料以及视频。
重点内容
Go语言简介
Go语言基础
开发环境
基础语法
标准库
知识点介绍
Go语言简介
Go是一个开源的编程语言,它很容易用于构建简单、可靠和高效的软件。
Go语言有着以下特点:
高性能、高并发
语言简单,学习曲线平缓
丰富的标准库
完善的工具链
静态链接
快速编译
跨平台
垃圾回收
Go语言基础
开发环境
官网安装Golang,目前的新版本已经可以自动配置环境变量
如果不想折腾,那直接安装Goland即可;我选择的是vscode+Go插件。这里需要注意的是在安装go相关依赖的时候可能会报错,需要在终端修改go环境参数配置
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn # 将代理设为国内源
修改后就可以根据vscode提示安装Go Tools相关。
ps:还可以安装一个Code Runner插件,一键运行
Go的开发环境就配置完毕,接下来就可以愉快的开启Go旅程。
基础语法
基础数据类型
整型
浮点数
复数
布尔型
字符串
变量
// 一般语法
var 变量名字 类型 = 表达式
// 简短变量声明
a := 100 // 只能在函数中使用
var a = 100
var a int
常量
// 一般语法
const 变量名字 类型 = 表达式
流程控制语句
循环控制语句只有for关键字,循环条件不需要括号,大括号必须放到for同一行,条件均可省略
// 死循环
for {
fmt.Println("loop")
break
}
for n := 0; n < 5; n++ {
if n%2 == 0 {
continue
}
fmt.Println(n)
}
// 只有终止条件
i := 1
for i <= 3 {
fmt.Println(i)
i = i + 1
}
条件分支语句包括 if-else 和 switch
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
// 这里的用法与C++不同
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
// Go中可以省略break
a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
case 4, 5:
fmt.Println("four or five")
default:
fmt.Println("other")
}
数组
数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,因此在Go语言中很少直接使用数组。
// 不同声明方法
var a [3]int
var a [3]int = [3]int{1, 2, 3}
a := [3]int{1, 2, 3}
a := [...]int{1, 2, 3}
切片
Slice(切片),它是可以增长和收缩的动态序列。一个slice由三个部分构成:指针、长度和容量。指针指向第一个slice元素对应的底层数组元素的地址;长度对应slice中元素的数目;长度不能超过容量,容量一般是从slice的开始位置到底层数据的结尾位置。
// 不同声明方法
var a []int
var a []int = []int{1, 2, 3}
a := []int{1, 2, 3}
a := make([]int, 3)
切片可以使用 append 函数向切片追加元素
a = append(a, 4)
Map
哈希表是一种巧妙并且实用的数据结构。它是一个无序的key/value对的集合,其中所有的key都是不同的,然后通过给定的key可以在常数时间复杂度内检索、更新或删除对应的value。
map类型可以写为map[K]V,其中K和V分别对应key和value。map中所有的key都有相同的类型,所有的value也有着相同的类型,但是key和value之间可以是不同的数据类型。其中K对应的key必须是支持==比较运算符的数据类型,所以map可以通过测试key是否相等来判断是否已经存在。
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
m := map[string]int{
"one": 1,
"two": 2,
}
使用 delete 函数删除元素
delete(m, "one")
结构体
结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。
type user struct {
name string
password string
}
// 成员声明方式
a := user{name: "wang", password: "1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}
c.password = "1024"
// 成员访问方式
var d user
d.name = "wang"
d.password = "1024"
JSON
JavaScript对象表示法(JSON)是一种用于发送和接收结构化信息的标准协议。
可以通过 Marshal 函数将结构体转换为JSON
type Movie struct {
Title string
Year int `json:"released"`
Color bool `json:"color,omitempty"`
Actors []string
}
data, err := json.Marshal(movies)
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
// 为了便于阅读,使用json.MarshalIndent函数
data, err := json.MarshalIndent(movies, "", " ")
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
通过 json.Unmarshal 函数完成将JSON解码为一个结构体
var titles []struct{ Title string }
if err := json.Unmarshal(data, &titles); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
fmt.Println(titles)
range遍历方式
利用 for-range 遍历复合数据类型
nums := []int{2, 3, 4}
sum := 0
for i, num := range nums {
sum += num
if num == 2 {
fmt.Println("index:", i, "num:", num) // index: 0 num: 2
}
}
fmt.Println(sum) // 9
函数
函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体。
func name(parameter-list) (result-list) {
body
}
Go中函数可以返回多个值
links, err := findLinks(url)
links, _ := findLinks(url) // 可以利用_占位不需要的返回值
拥有函数名的函数只能在包级语法块中被声明,通过函数字面量(function literal),我们可绕过这一限制,在任何表达式中表示一个函数值。函数字面量的语法和函数声明相似,区别在于 func 关键字后没有函数名。函数值字面量是一种表达式,它的值被称为匿名函数(anonymous function)。
// squares返回一个匿名函数。
// 该匿名函数每次被调用时都会返回下一个数的平方。
func squares() func() int {
var x int
return func() int {
x++
return x * x
}
}
func main() {
f := squares()
fmt.Println(f()) // "1"
fmt.Println(f()) // "4"
fmt.Println(f()) // "9"
fmt.Println(f()) // "16"
}
参数数量可变的函数称为可变参数函数。在声明可变参数函数时,需要在参数列表的最后一个参数类型之前加上省略符号“...”,这表示该函数会接收任意数量的该类型参数。
func sum(vals ...int) int {
total := 0
for _, val := range vals {
total += val
}
return total
}
指针
Go中指针主要用于对函数传参时,函数默认按值传递,函数内部对变量的更改不会影响函数外,如果需要改变,需要通过指针传递
func add2(n int) {
n += 2
}
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 5
add2(n)
fmt.Println(n) // 5
add2ptr(&n)
fmt.Println(n) // 7
}
标准库
fmt
包实现了类似C语言printf和scanf的格式化I/O。
import "fmt"
s := "hello"
n := 123
p := point{1, 2}
fmt.Println(s, n) // hello 123
fmt.Println(p) // {1 2}
fmt.Printf("s=%v\n", s) // s=hello
fmt.Printf("n=%v\n", n) // n=123
fmt.Printf("p=%v\n", p) // p={1 2}
fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
f := 3.141592653
fmt.Println(f) // 3.141592653
fmt.Printf("%.2f\n", f) // 3.14
time
包含时间处理的函数。
import (
"fmt"
"time"
)
now := time.Now()
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
fmt.Println(t) // 2022-03-27 01:25:36 +0000 UTC
t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
diff := t2.Sub(t)
fmt.Println(diff) // 1h5m0s
fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
strconv
strconv包实现了基本数据类型和其字符串表示的相互转换。
import (
"fmt"
"strconv"
)
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) // 1.234
n2, _ := strconv.Atoi("123")
fmt.Println(n2) // 123
env
os包提供了操作系统函数的不依赖平台的接口。
exec包执行外部命令。它包装了os.StartProcess函数以便更容易的修正输入和输出,使用管道连接I/O,以及作其它的一些调整。
import (
"fmt"
"os"
"os/exec"
)
// go run example/20-env/main.go a b c d
fmt.Println(os.Args) // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
fmt.Println(os.Setenv("AA", "BB"))
// 用于使用给出的参数执行第一个参数指定的程序
buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
if err != nil {
panic(err)
}
fmt.Println(string(buf)) // 127.0.0.1 localhost
总结
收获
本文主要介绍了Go语言的基本语法,与C++等语言有相类似的地方,但是也有很多的区别,比如变量类型后置等都不太习惯。如果有其他语言的基础,Go的基本语法还是比较容易理解的,不过还是要勤加练习才能熟练掌握。
问题
对一些标准库和API接口还是比较陌生,后续通过项目实战。
引用
Go语言圣经(中文版)前言 · Go语言圣经 (studygolang.com)