Go语言基础-基础语法 | 青训营笔记

142 阅读7分钟

Go语言基础-基础语法 | 青训营笔记

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

前言

本文主要介绍Go语言的基础语法,除了上课内容外,还包括我看的其他资料以及视频。

重点内容

  • Go语言简介

  • Go语言基础

    • 开发环境
    • 基础语法
    • 标准库

知识点介绍

Go语言简介

Go是一个开源的编程语言,它很容易用于构建简单、可靠和高效的软件。

Go语言有着以下特点:

  1. 高性能、高并发
  2. 语言简单,学习曲线平缓
  3. 丰富的标准库
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收

Go语言基础

开发环境

  1. 官网安装Golang,目前的新版本已经可以自动配置环境变量

  2. 如果不想折腾,那直接安装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接口还是比较陌生,后续通过项目实战。

引用