0%

Go语言基础

古人学问无遗力,少壮工夫老始成。纸上得来终觉浅,绝知此事要躬行。 —— 宋.陆游 《冬夜读书示子聿》


Go语言Mac下环境搭建

一、安装

  1. 通过安装包

    • 下载安装文件
    • 一路双击安装就行了
    • 配置环境变量vim ~/.bash_profile
    1
    2
    3
    4
    #注意不要和GOROOT相同,否则会报warning: GOPATH set to GOROOT (/usr/local/go) has no effect
    export GOPATH=~/go #GOPATH是自定义的工作空间,它是日常开发的根目录。
    export GOBIN=/usr/local/go/bin
    export PATH=$PATH:$GOBIN
  2. 通过Homebrew安装

    • brew install go
  3. 相关命令

    • 查看版本go version
    • 查看环境变量go env

二、测试

  1. vim main.go

  2. 写入以下脚本

1
2
3
4
5
6
7
package main
import("fmt")

func main(){
fmt.Println("It works!")
}

  1. 生成可执行文件 go build main.go
  2. 运行可执行文件 ./main

三、vs code开发配置

  1. vs code开发时自动导入包

    • command+shift+P
    • go:install/update tools
    • 将所有16个插件都勾选上安装
  2. vs code安装tools超时失败

    • 自动下载:go env -w GO111MODULE=on
    • 设置代理:go env -w GOPROXY=https://goproxy.cn,direct

Go语言env说明

一、概念

go env命令用于打印Go语言的环境变量信息,也可进行设置环境变量信息。

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
GO111MODULE="" 
GOARCH="amd64"
GOBIN=""
GOCACHE="***"
GOENV="***"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="***"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/14/kx973qc93bn0kpgymsb01xbh0000gn/T/go-build470845532=/tmp/go-build -gno-record-gcc-switches -fno-common"

二、常用

参数名 含义
GCCGO 构建时时候所用编译器
GOARCH 计算机处理器的架构(常见如amd64、arm等)
GOCACHE 存储编译后信息的缓存目录
GOFLAGS go命令能够识别的标记(可以是多个,中间用空格隔开)
GOOS 编译代码的操作系统名称(如linux、windows、darwin等),用于交叉编译
GOPROXY go module目录所在的地址
GOROOT Go语言的安装目录的路径
GOPATH 工作区所在的路径(可存在多个工作区),用于存放源代码、归档和可执行文件
GOBIN 存放可执行文件的路径
GOTOOLDIR Go工具目录的绝对路径
CGO_ENABLED 指明cgo工具是否可用的标识
GOEXE 可执行文件的后缀
GOHOSTARCH 程序运行环境的目标计算架构
GOHOSTOS 程序运行环境的目标操作系统

三、参考

  1. 参考一
  2. 参考二

Go语言package和import

一、概念

      Go语言使用包package来管理定义模块,使用import导入模块。

  1. 基本原则
    • 同一个目录下的所有源码文件的代码包声明语句要相同,如果包含命令源码文件,则其他源码文件包声明都应为main
    • 代码包的名称与目录名称可以不一致,代码里引用时需要使用代码包名称,build时使用的是目录名称
      • 源码文件所在的目录相对于src目录的相对路径就是导入路径,而实际使用时给定的限定符要与它声明代码包名称对应

二、使用

  1. 命名重复的包名

    • 目录结构($GOPATH/src/pack),tree
    1
    2
    3
    4
    5
    6
    7
    8
    9
     .
    ├── go.mod
    ├── main.go
    ├── p1
    │ ├── demo1.go
    │ └── demo2.go
    └── p2
    ├── demo1.go
    └── demo2.go
    • p1/demo1.go
    1
    2
    3
    4
    5
    6
    package p2

    import "fmt"
    func Test1(){
    fmt.Println("p1.test1")
    }
    • p1/demo2.go
    1
    2
    3
    4
    5
    6
    package p2

    import "fmt"
    func Test2(){
    fmt.Println("p1.test2")
    }
    • p2/demo1.go
    1
    2
    3
    4
    5
    6
    package p2

    import "fmt"
    func Test1(){
    fmt.Println("test1")
    }
    • p2/demo2.go
    1
    2
    3
    4
    5
    6
    package p2

    import "fmt"
    func Test2(){
    fmt.Println("test2")
    }
    • main.go
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package main

    import (
    p1 "pack/p1"
    // p2 "pack/p2"
    )
    func main(){
    // p2.Test1()
    // p2.Test2()

    p1.Test1()
    p1.Test2()
    }

三、参考

  1. 参考一
  2. 参考二
  3. 参考三

Go语言for循环

一、基础

  1. 标准三段式
  2. for ... range方式
    • 遍历数组
      • 省略索引
      • 不省略索引格式
    • 遍历切片
      • 省略索引
      • 不省略索引格式
    • 遍历字典
      • key不固定
    • 遍历通道
      • 只有一个变量
  3. 无限循环方式for{...}

二、使用

  1. 标准三段式
1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
sum := 0
for i := 0; i <= 100; i++ {
sum += i
}
fmt.Println(sum)
}
  1. for ... range形式

    • 遍历数组
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package main

    import "fmt"

    func main() {
    array := [...]int64{1, 2, 3, 4}

    for k, v := range array {
    fmt.Println(k, v)
    }
    fmt.Println()

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

    for i:=0; i<len(array); i++ {
    fmt.Println(array[i])
    }
    }
    • 遍历切片
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package main

    import "fmt"

    func main() {
    slice1 := []string{"liu", "liusir", "liuliu"}

    for index,value := range slice1{
    fmt.Printf("index:%d, value:%s\n", index, value)
    }

    for _,value := range slice1{
    fmt.Printf("value:%s\n",value)
    }

    for i:=0; i<len(slice1); i++ {
    fmt.Printf("index:%d, value:%s\n", i, slice1[i])
    }
    }
    • 遍历字典
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package main

    import "fmt"

    func main() {
    map1 := map[string]interface{}{"Name":"liu", "Age":30, "Male": true, "Hobby":[]string{"跑步", "电影"}}

    fmt.Printf("length of map1:%d\n", len(map1))

    for key,value := range map1{
    fmt.Println(key,value)
    }
    }
    • 遍历通道
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package main

    import (
    "fmt"
    "time"
    )

    func main() {
    ch := make(chan int)
    go func() {
    for i := 1; i <= 5; i++ {
    ch <-i
    time.Sleep(time.Second)
    }
    close(ch)
    }()

    for value := range ch{
    fmt.Println(value)
    }
    }
  2. 无限循环方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"time"
)

func main() {
i := 1
for {
fmt.Println(i)
time.Sleep(time.Second)
i++
}
}

三、参考


Go语言基本数据类型

一、概念

  1. 在Go编程语言中,数据类型用于声明函数和变量,它的出现是为了合理分配利用内存,根据需求把数据分成不同的内存大小,避免不必要的内存浪费。

  2. Go语言数据类型包含基本类型和复合类型两大类。

    • 基本数据类型包括:布尔型、字符串型、数值型(整型、浮点型、复数型)、错误类型。
    • 复合数据类型包括:数组、结构体、指针、切片、字典(也称map)、通道、函数、接口。
      • 切片、字典类型、通道类型、函数类型等属于引用类型,数组、其他基础数据类型、结构体类型等属于值类型
  3. 声明变量的几种方式

    • 标准声明方式var str string
    • 类型推断方式var str = "liusir"
    • 短变量声明方式str := "liusir"
      • 类型推断 + 语法糖
      • 只能在函数体内部使用短变量声明
      • 优点
        • 提升程序的灵活性
        • 重构代码变得更加简单
        • 提高程序的可维护性
        • 类型推断在代码编译时确定,不影响运行效率
          • 动态语言是运行时确定变量类型,静态语言是编译时确定变量类型
  4. 变量的重声明

    • 重声明的类型必须与原本的类型相同
    • 变量的重声明只可能发生在某一个代码块中
    • 必须要使用短变量的声明方式
    • 声明并赋值的变量必须是多个,并且至少有一个是新的变量
      • 常用于err的重声明
  5. 可重名变量

    • 在同一个代码块中不允许出现重名的变量
    • 可重名变量的查找过程
      • 优先查找当天作用域代码块
      • 当前代码块没有则向外查找
      • 直至最外层仍找不到则报错
        • 默认不查找import的包,特殊情况import . "XXX"除外

二、使用

  1. map并发安全写
  2. 可重名变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

var value = "var1"

func main() {
test(value)
// value :="liusir"
value := "var2"
{
value := "var3"
fmt.Printf("The value is %s.\n", value)
}
fmt.Printf("The value is %s.\n", value)
test(value)
}

func test(name string) {
fmt.Printf("The value is %s.\n", name)
}

三、参考

  1. 参考一

Go语言切片和数组

一、概念

  1. 数组:是同一类型元素的集合,Go语言中不允许混合不同类型的元素(interface{}类型数组可以包含任意类型)。

    • 数组是值类型,意为当把数组a赋值给一个新的变量b时,变量b得到的只是一个原始数组a的副本,如果对新变量b进行更改则不会影响原始数组a。
    • 数组的下标从0开始.
    • 数组类型的值长度是固定的
      • 数组的长度在声明它的时候就必须给定,并且之后不会再改变
    • 声明数组,如长度为4的int型数组var arr [4]int
  2. 切片:是由数组建立的一种方便、灵活且功能强大的包装,它本身不拥有任何数据,只是对现有数组的引用。

    • 切片是引用类型,同值类型刚好相反。
    • 切片类型的值长度是可变长的
    • 声明切片
      • 方式一:使用varvar s1 []int
      • 方式二:使用makevar s2 = make([]int, len, cap),cap可选
        • 使用make函数初始化切片时,如果不指明其容量,那么它就会和长度一致。
        • 切片的容量实际上代表了它的底层数组的长度(仅限于方式一和方式二的情形)
      • 方式三:基于数组或切片s4 := s3[3:6]或s4 := arr[3:6],其中[3,6)
        • 长度=6-3
        • 容量=8-3(假设s3的容量为8)
    • 扩容
      • 扩容时不会改变原来的切片,而是会生成一个容量更大的切片,然后将把原有的元素和新元素一并拷贝到新切片中
      • 扩容后的容量:<1024则为2倍(极端情况追加后比2倍还大则以新切片为准),>=1024则为1.25倍
    • 缩容
      • 切片缩容之后还是会引用底层的原数组,有时候会造成大量垃圾内容没有被回收
  3. 联系与区别

    • 每个切片的底层数据结构中一定会包含一个数组,数组可以被叫做切片的底层数组,而切片则可以被看作是对数组的某个连续片段的引用
    • 通过函数len可得到数组和切片的长度,通过函数cap可得到数组和切片的容量
      • 数组的容量永远等于长度且都是不可变的

二、使用

  1. 数组

    • 声明方式

      • 只声明不初始化var a [3]int,此时a的所有元素都被赋值为整型0

      • 声明并初始化(必须初始化所有元素)

        • 简略模式声明:a := [3]int{1, 2, 3},也可初始化部分元素:a := [3]int{1}
        • 简略模式声明时不指定长度a := [...]int{12, 78, 50},编译器会自动计算
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
           // 先声明再赋值
        var arr [3]int
        arr[0] = 12
        arr[1] = 78
        arr[2] = 50

        // 声明并赋值
        // var arr [5]int = [5]int{1,2,3,4,5}

        // 简略模式
        // arr := [5]int{1,2,3,4,5}

        // 省略长度
        // arr := [...]int{1,2,3,4,5}

        // 只初始化部分
        // arr := [5]int{1:1,4:4}
    • 数组长度len(a)

    • 遍历数组

      • 使用for循环
      • 使用range返回key,value,key可省略
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package main
    import "fmt"
    func main() {
    c := [...]int{1, 2}
    for i := 0; i < len(c); i++ {
    fmt.Printf("第 %d 个元素为:%d\n", i, c[i])
    }
    for i, v := range c {
    fmt.Printf("第 %d 个元素为:%d\n", i, v)
    }
    }
  2. 切片

    • 声明方式

      • 基于数组arr创建
        • arr[:]
      • 简略模式创建
      • 使用make创建
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      func main() {

      b := [5]int{76, 77, 78, 79, 80}
      var c []int = b[:]
      var d []int = b[1:4]
      fmt.Println(c)
      fmt.Println(d)

      e := []int{6, 7, 8} // creates and array and returns a slice reference
      fmt.Println(e)

      f := make([]int, 5, 5)
      fmt.Println(f)
      }
    • 追加元素append(slice, element)

      • ...运算符
    • 切片长度len(slice)

    • 切片容量cap(slice)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
)

func main() {
s3 := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
fmt.Println(s3)

s4 := s3[4:6]
fmt.Println(s4)

// 引用类型,s4改变会影响s3
s4 = append(s4, 98)
fmt.Println(s4)

fmt.Println(s3)
}


三、参考

  1. 参考一
  2. 参考二

Go语言字典map

一、基础

  1. 字典类型map[keyType]valueType,map的读取和设置也是通过下标key来操作

    • map的数据类型更多,slice则只能通过int型的下标操作
    • 声明和初始化
      • 方式一:先声明,后初始化var m1 map[string]int m1= make(map[string]int)
      • 方式二:声明并初始化赋值var m1 map[string]int=map[sting]int{"key":0}
      • 方式三:使用makem2:=make(map[string]int)
  2. 字典底层实现/usr/local/go/src/runtime/map.go(go version go1.18.1 darwin/amd64)

    • Go语言的字典类型其实是一个哈希表(hash table)的特定实现
    • 在Go语言的字典中,每一个键值都是由它的哈希值代表的
    • Go语言字典的键类型不可以是函数类型、字典类型和切片类型
      • Go语言规范规定在键类型的值之间必须可以施加操作符==!=,即键类型的值必须要支持判等操作
      • 如果键的类型是接口类型的,那么键值的实际类型也不能是上述三种类型,否则在程序运行过程中会引发panic
        • 接口类型尽量不要作为键,使用不当会引发运行时panic
      • 如果键的类型是数组类型,那么还要确保该类型的元素类型不是函数类型、字典类型或切片类型
      • 如果键的类型是结构体类型,那么还要保证其中字段的类型的合法性
    • 只有键的哈希值和键值都相等,才能说明查找到了匹配的键-元素对
  3. 哈希函数

  4. 哈希碰撞

  5. 装载因子(负载因子)

  6. 扩容

二、使用

  1. 基础demo
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
package main

import (
"fmt"
"sort"
)

func main(){
myMap := make(map[string]int)

myMap["liu"] = 5
myMap["sir"] = 2
myMap["dot"] = 3
myMap["me"] = 4

fmt.Println(myMap)

var mySlice []string

for k := range myMap {
mySlice = append(mySlice, k)
}
// 每次打印顺序都不一定
fmt.Println(mySlice)
// 按字典序
sort.Strings(mySlice)
fmt.Println(mySlice)


// var arr [5]int = [5]int{1,2,3,4,5}
// arr := [5]int{1,2,3,4,5}
// arr := [...]int{1,2,3,4,5}
arr := [5]int{1:1,4:4}
// var multiMap map[int]interface{}
multiMap := make(map[int]interface{})
multiMap[0] = false
multiMap[4] = 1
multiMap[1] = arr
multiMap[2] = "liusir"
fmt.Println(multiMap)
}
  1. demo2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
)

func main() {
m := make(map[string]string)
m["a"] = "a"
m["b"] = "b"
// fmt.Println(m)
if v, ok := m["c"]; ok {
fmt.Println(v)
} else {
fmt.Println(ok)
fmt.Println("not found")
}

for k, v := range m {
fmt.Println(k, v)
}
}

三、参考


Go语言信道channel

一、概念

      信道是goroutine之间通信的一种方式,可以在信道两端进行操作。

二、使用

三、参考

  1. 参考一

Go语言结构体struct

一、概念

      和C语言类似,结构体是一种用户自定义的可用的数据类型,它允许存储不同类型的数据项。同时结构体是一种值类型,在不包含引用类型的情况下可以进行等值==比较。

  1. 分类

    • 命名结构体
    • 匿名结构体
  2. 访问控制

    • 结构体内部变量的首字母大写是公开变量,无访问限制。
    • 结构体内部变量的首字母小写是内部变量,只有属于同一个package的代码才能直接访问。
  3. 结构体类型定义方法,表明该方法的适用对象是当前结构体,具体做法是在func关键字和方法名之间加入接收者

    • 值类型与值方法
    • 指针类型与指针方法
  4. 进阶

    • 空结构体及其应用
      • struct{}是空结构体类型,struct{}{}是对它的实例化
      • 空结构体也是结构体,但不占内存
    • 结构体匿名字段
    • 嵌套结构体
      • 方法和字段提升
      • 屏蔽现象

二、使用

  1. 声明结构体
1
2
3
4
5
6
7
8
9
10
type 结构体名 struct {
属性名1 类型 `tag`
属性名2 类型 `tag`
}

如:
type Person struct {
Name string
age int
}
  1. 创建结构体变量

    • 直接声明:var p1 Person
    • 用大括号:p2 := Person{}
    • new方式:var p3Point = new(Person)
    • &方式:var p4Point = &Person{"王5", 22}
  2. 定义方法

1
2
3
4
5
6
7
8
9
type Person struct {
Name string
age int
}

func (p Person) say() float64 {
fmt.Println("hello")
return 1.1
}

三、参考

  1. 参考一
  2. 参考二
  3. 参考三
  4. 参考四

Go语言接口interface

一、基础

      在面向对象编程领域里,接口一般这样定义:接口定义一个对象的行为。接口只指定了对象应该做什么,至于如何实现这个行为则由对象本身去确定。由于Go不支持类class和面向对象编程(可以以别的方式实现类似的功能),所以在Go语言中,接口就是方法签名的集合。接口指定了一个类型应该具有的方法,并由该类型决定如何实现这些方法。

  1. 对于任何数据类型,只要它的方法集合中完全包含了一个接口的全部的方法,那么它就一定是这个接口的实现类型。

    • 方法签名要完全一致
    • 方法名称要完全一致
  2. 接口变量

    • 值为nil的情况
      • 只声明而不初始化
      • 显式地赋值为nil

二、使用

三、参考

  1. 参考一
  2. 参考二

Go语言多返回值

一、概念

      一个函数的函数名即是该函数的代表,也是一个变量。由于函数名变量通常用来把函数的处理结果数据返回给调用函数,即递归调用,所以一般把函数名变量称为返回值,函数的返回值类型是在定义函数时指定的。

      Go一个非常特别的特性(对于编译语言而言)是函数和方法可以返回多个值(Python和Perl同样也可以)。这可以用于改进一大堆在C程序中糟糕的惯例用法:修改参数的方式,返回一个错误(例如遇到EOF则返回-1)。Go函数的返回值或者结果参数可以指定一个名字(名字不是强制的,但是它们可以使得代码更加健壮和清晰),并且像原始的变量那样使用,就像输入参数那样。如果对其命名,在函数开始时,它们会用其类型的零值初始化。如果函数在不加参数的情况下执行了return语句,结果参数会返回。用这个特性,允许(再一次的)用较少的代码做更多的事。

二、使用

  1. 普通例子

    • 代码(test.go)
1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func multiReturn() (a int, b int, c int) {
a, b, c = 111, 222, 333
return
}

func main() {
a, b, c := multiReturn()
fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}
  • 运行
    • 方式一:go run test.go
    • 方式二:go build test.go && ./test
    • 方式三:在线运行
  1. 错误处理

三、参考

  1. 参考一

Go语言断言

一、基础

      Golang中的所有程序都实现了interface{}的接口,这意味着所有的类型如string,int,int64甚至是自定义的struct类型都就此拥有了interface{}的接口,这种做法和Java中的Object类型比较类似。

二、使用

三、参考

  1. 参考一

Go语言反射

一、基础

      编写好的程序在编译时,变量会被转换为内存地址,变量名不会被编译器写入到可执行部分,所以在运行程序时程序无法获取自身的信息。支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。简而言之,反射就是指在程序运行期对程序本身进行访问和修改的能力。

      Golang中提供了一种机制在运行时更新和检查变量的值、调用变量的方法和变量支持的内在操作,但是在编译时并不知道这些变量的具体类型,这种机制被称为反射。

二、使用

三、参考

  1. 参考一
  2. 参考二
  3. 参考三
  4. 参考四
  5. 参考五
  6. 参考六

Go语言延迟调用defer

一、概念

      defer是Go语言中的延迟执行语句,用来添加函数结束时执行的代码,常用于释放某些已分配的资源、关闭数据库连接、断开socket连接、解锁一个加锁的资源等。Go语言机制能够保证一定会执行defer语句中的代码,有点类似于JavaC#语言里的finally语句,C++PHP语言里的析构函数等。

defer后面必须是函数调用语句,其后面的函数会当前函数执行结束后被调用。

二、使用

  1. demo
1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func testFunc() {
fmt.Println("执行结束...")
}

func main() {
defer testFunc()

fmt.Println("开始执行...")
}

三、参考

  1. 参考一

Go语言异常处理panic

一、概念

      panic是Go语言中用于终止程序的一种函数,一般出现在程序出现了很大的故障,如不能在提供服务了,或程序在运行阶段碰到了内存异常的操作,如空指针的取值,改写只读内存等。


Go语言交叉编译

一、概念

      很多时候会有这样的场景出现,使用Mac开发或使用Windows开发,需要编译成Linux系统的执行文件,此时就需要用到交叉编译。在1.5+版本后(通过自举),Go程序编译时可通过设置环境变量实现交叉编译,从而大大提高了开发效率。

编程语言的自举就是完全依靠自身语言(如go)写出自己的编译器、解释器来编译自身。

二、使用

  1. 可通过go tool dist list查看当前版本支持的系统和架构

常见如下:

GOOS GOARCH
darwin 386
darwin amd64
darwin arm
darwin arm64
dragonfly amd64
freebsd 386
freebsd amd64
freebsd arm
linux 386
linux amd64
linux arm
linux arm64
linux ppc64
linux ppc64le
netbsd 386
netbsd amd64
netbsd arm
openbsd 386
openbsd amd64
openbsd arm
plan9 386
plan9 amd64
solaris amd64
windows 386
windows amd64
  1. 使用
    • main.go
1
2
3
4
5
6
7
8
9
10
package main

import (
"fmt"
"runtime"
)

func main() {
fmt.Printf("OS: %s\nArchitecture: %s\n", runtime.GOOS, runtime.GOARCH)
}
  • windows/linux/mac
    • CGO_ENABLED=0 GOOS=window GOARCH=amd64 go build main.go
    • CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
    • CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go
  1. 默认情况下Go环境变量CGO_ENABLED=1(可通过go env查看),即默认开启cgo,意为允许在Go代码中调用C代码。
    • 要使用CGO特性,需要安装C/C++构建工具链,在MacOS和Linux下是要安装GCC,在windows下是需要安装MinGW工具,同时需要保证环境变量CGO_ENABLED被设置为1,这表示CGO是被启用的状态。
    • 交叉编译时CGO默认是禁止的,亦可在编译时指定CGO_ENABLED=0。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"fmt"
"runtime"
)

/*
#include <stdio.h>

void sayHello(){
int a;
printf("请输入数字:");
scanf("%d", &a);
printf("a的值为:%d", a);
}
*/
import "C"

func main() {
fmt.Printf("OS: %s\nArchitecture: %s\n", runtime.GOOS, runtime.GOARCH)
C.sayHello()
}

CGO_ENABLED=1 go run main.go

CGO_ENABLED=0 go run main.go

三、参考

  1. 参考一

Go语言go build / go install / go run / go get / go mod

  1. go build
    • 对于命令源码文件,执行后会在当前目录下生成可执行文件
      • 执行go build时指定文件名如index.go,生成的可执行文件与命令源码文件名称相同如index
      • 执行go build时不指定文件名,生成的可执行文件与命令源码文件所在目录名相同如goapp
    • 对于库源码文件,执行后不会在当前目录生成任何文件(可通过-x参数查看构建细节),主要用于验证和检查
  2. go install:约等于go build + 链接
    • 对于命令源码文件,执行后会在$GOPATH/bin/目录下生成可执行文件
    • 对于库源码文件,执行后会在$GOPATH/pkg/darwin_amd64(平台)/目录下生成归档文件
  3. go run:只能用于main包,在临时目录生成可执行文件并执行
  4. go get:约等于git clone + go install
  5. go mod:go1.11新增特性,用于替代老版本必须将项目放在GOPATH下,不方便管理
    • 相关的三个参数设置GO111MODULE/GOPRIVATE/GOPROXY
      • go env -w GO111MODULE=off/on/auto

Go语言常见web框架

一、概念

      软件框架(software framework),通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品。Web框架,全称web应用框架(Web application framework)是一种开发框架,用来支持动态网站、网络应用程序及网络服务的开发。

二、常见

  1. Beego
  2. Gin
  3. Iris
  4. Echo
  5. Buffalo
  6. Revel

三、参考

  1. 参考一
  2. 参考二

Go语言type关键字

一、用法

  1. 定义结构体
  2. 定义接口
  3. 别名类型
    • type MyString = string代表MyString是string类型的别名类型
      • type MyString2 string则代表MyString2和string是两个不同的类型,是对string的再定义
    • Go默认的别名类型:byteuint8的别名类型,而runeint32的别名类型
    • 源类型和别名类型是两个对立的概念
  4. 类型重定义
    • type MyString2 string代表MyString2和string是两个不同的类型,是对string的再定义,此时称string为MyString2的潜在类型
    • 潜在类型相同的不同类型的值之间是可以进行类型转换的
    • 两个不同类型的潜在类型相同,它们的值之间不能进行判等或比较,它们的变量之间也不能赋值
  5. 类型判断开关(type switch)

二、例子

三、参考


Go语言sync.WaitGroup

一、基础

  1. 引入,运行100个协程
    • 不做任何处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"fmt"
"sync"
)
func main() {
counter := 0
for i := 0; i < 100 ; i++{
go func(i int) {
counter += 1
fmt.Println("Goroutine ",i)
}(i)
}
fmt.Println(counter)
}
  • 加入sleep休眠(休眠要足够长以保证100个协程运行结束)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
"time"
)
func main() {
counter := 0
for i := 0; i < 100 ; i++{
go func(i int) {
counter += 1
fmt.Println("Goroutine ",i)
}(i)
}
time.Sleep(time.Second * 5)
fmt.Println(counter)
}
  • 使用管道channel(大材小用),10k+导致内存飙升
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
package main

import (
"fmt"
"sync"
)

func main() {

counter := 0
ch := make(chan int)
count := 100

for i := 0; i < 100 ; i++{
go func(i int) {
counter += 1
fmt.Println("Goroutine ",i)
ch <- 0
}(i)
}

for range ch {
count--
if count == 0 {
close(ch)
}
}

fmt.Println(counter)
}
  • 使用sync.WaitGroup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"sync"
)

func main() {
counter := 0
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100 ; i++{
go func(i int) {
counter += 1
fmt.Println("Goroutine ",i)
wg.Done()
}(i)
}
wg.Wait()
fmt.Println(counter)
}

二、详解

      WaitGroup顾名思义,就是用来等待一组操作完成的。WaitGroup内部实现了一个计数器,用来记录未完成的操作个数。它提供了三个方法

  • wg.Add(n)用来添加计数
  • wg.Done()用来在操作结束时调用,使计数减一。
  • wg.Wait()用来等待所有的操作结束,即计数置为为0
    • 该函数会在计数不为0时等待,在计数为0时立即返回。
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
package main

import (
"fmt"
"sync"
)

func main() {
counter := 0
wg := sync.WaitGroup{}
wg.Add(100)
for i := 0; i < 100; i++ {
counter += 1
// 传参必须为&wg
go print(i, &wg)
}
wg.Wait()
fmt.Println(counter)
}

// 一定要通过指针传值
func print(i int, wg *sync.WaitGroup) {
fmt.Println(i)
wg.Done()
}

三、参考


Go语言类型断言

一、基础

  1. 方式一:x.(T)

    • x:必须是接口类型
      • interface{}代表空接口,任何类型都是它的实现类型
        • 空接口类型interface{},不包含任何方法定义、
          • 空结构体类型struct{},不包含任何字段和方法的、
          • 空的切片值[]string{}
          • 空的字典值map[int]string{}
        • 任何类型的值都可以很方便地被转换成空接口的值,即interface{}(var)
    • T:可以是接口类型,也可以是非接口类型
  2. 方式二:switch t := x.(type)语句

    • x:必须是接口类型

二、使用

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
package main

import (
"fmt"
)

var container = []string{"beijng", "shanghai", "guangzhou"}

func main() {
container := map[int]string{0: "liu", 1: "liusir", 2: "liuliu"}
fmt.Printf("The element is %q.\n", container[1])

// value, ok := interface{}(container).([]string)
value, ok := interface{}(container).(map[int]string)
// interface{}(container)意为将container转换为空接口interface{}
// .(map[int]string)用于类型断言,即interface{}(container)是否为map[int]string类型
if ok {
fmt.Println(value)
} else {
fmt.Println(ok)
}

var x interface{}=10
// v,ok := x.(int)
v,ok := x.(string)
if ok {
fmt.Println(v)
} else {
fmt.Println(ok)
}

// container := 1

var elem string
var err error
switch t := interface{}(container).(type) {
case []string:
elem = t[1]
case map[int]string:
elem = t[1]
default:
err = fmt.Errorf("unsupported container type: %T", container)
fmt.Println(err)
return
}

fmt.Printf("The element is %q. (container type: %T)\n", elem, container)
}

三、参考


Go语言访问权限规则

一、分类

  1. 包级私有
  2. 包级公开
  3. 模块级私有(internal关键字)

二、使用


make和new

一、基础

  1. make是专门用来创建slice、map、channel的,它返回的是被创建的值,并且立即可用。
  2. new是申请一小块内存并标记它是用来存放某个值的,它返回的是指向这块内存的指针,而且这块内存并不会被初始化。
    • 对于引用类型的值不要用new,能用make就用make,不能用make就用复合字面量来创建

二、使用

三、参考


Go语言标准库

一、基础

      在Go语言的安装文件里包含了一些可以直接使用的包,即标准库。Go语言的标准库提供了清晰的构建模块和公共接口,包含I/O操作、文本处理、图像、密码学、网络和分布式应用程序等,并支持许多标准化的文件格式和编解码协议。

Go语言标准库包名 说明
bufio 带缓冲的 I/O 操作
bytes 实现字节操作
container 封装堆、列表和环形列表等容器
crypto 加密算法
database 数据库驱动和接口
debug 各种调试文件格式访问及调试功能
encoding 常见算法如 JSON、XML、Base64 等
flag 命令行解析
fmt 格式化操作
go Go语言的词法、语法树、类型等。可通过这个包进行代码信息提取和修改
html HTML 转义及模板系统
image 常见图形格式的访问及生成
io 实现 I/O 原始访问接口及访问封装
math 数学库
net 网络库,支持 Socket、HTTP、邮件、RPC、SMTP 等
os 操作系统平台不依赖平台操作封装
path 兼容各操作系统的路径操作实用函数
plugin Go 1.7 加入的插件系统。支持将代码编译为插件,按需加载
reflect 语言反射支持。可以动态获得代码中的类型信息,获取和修改变量的值
regexp 正则表达式封装
runtime 运行时接口
sort 排序接口
strings 字符串转换、解析及实用函数
time 时间接口
text 文本模板及 Token 词法器

二、使用

  1. container/list
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"container/list"
"fmt"
)


func main() {
l := list.New()

l.PushFront("liu")
l.PushBack("sir")
element := l.PushBack("liuliu")
l.InsertBefore("long", element)
l.InsertAfter("me", element)

// l.Remove(element)

for i := l.Front(); i != nil; i = i.Next() {
fmt.Println(i.Value)
}
}

三、参考


Go语言any关键字

一、基础

      golang 1.18新的关键字any,它其实是空接口interface{}的别名,即type any = interface{}。引入any关键字的好处之一就是使用泛型时简化代码的书写。

二、使用

三、参考


Go语言泛型

一、基础

二、使用

三、参考


Go语言占位符

一、基础

占位符 类型 说明
%v 普通占位符 相应值的默认格式
%+v 普通占位符 打印结构体时,会添加字段名
%#v 普通占位符 相应值的Go语法表示
%T 普通占位符 相应值的类型的Go语法表示
%% 普通占位符 字面上的百分号,并非值的占位符
%t 布尔占位符 true 或 false
%b 整数占位符 二进制表示
%c 整数占位符 相应Unicode码点所表示的字符
%d 整数占位符 十进制表示
%o 整数占位符 八进制表示
%q 整数占位符 单引号包裹的字符字面值,由Go语法安全地转义
%x 整数占位符 十六进制表示,字母形式为小写a-f
%X 整数占位符 十六进制表示,字母形式为大写A-F
%U 整数占位符 Unicode格式:U+1234,等同于 “U+%04X”
%b 浮点数和复数 无小数部分的指数为二的幂的科学计数法,与strconv.FormatFloat的’b’转换格式一致
%e 浮点数和复数 科学计数法
%E 浮点数和复数 科学计数法
%f 浮点数和复数 有小数点而无指数
%g 浮点数和复数 根据情况选择%e或%f以产生更紧凑的(无末尾的0)输出
%G 浮点数和复数 根据情况选择%E或%f以产生更紧凑的(无末尾的0)输出
%s 字符串与字节切片 输出字符串表示(string类型或[]byte)
%q 字符串与字节切片 双引号围绕的字符串,由Go语法安全地转义
%x 字符串与字节切片 十六进制,小写字母,每字节两个字符
%X 字符串与字节切片 十六进制,大写字母,每字节两个字符
%p 指针 十六进制表示,前缀 0x
+ 其他 总打印数值的正负号 对于%q(%+q)保证只输出ASCII编码的字符。
- 其他 在右侧而非左侧填充空格(左对齐该区域)
# 其他 为八进制添加前导0(%#o) 为十六进制添加前导0x(%#x)或 0X(%#X) 为%p(%#p)去掉前导0x等
‘ ‘ 其他 (空格)为数值中省略的正负号留出空白(% d); 以十六进制(% x, % X)打印字符串或切片时,在字节之间用空格隔开
0 其他 填充前导的0而非空格;对于数字这会将填充移到正负号之后

说明

  • go没有’%u’点位符,若整数为无符号类型,默认就会被打印成无符号的。
  • 宽度与精度的控制格式以Unicode码点为单位。宽度为该数值占用区域的最小宽度;精度为小数点之后的位数。
    • 操作数的类型为int时,宽度与精度都可用字符’*’表示。
    • 对于%g、%G而言,精度为所有数字的总数,例如:123.45,%.4g会打印123.5,而%6.2f会打印123.45。
    • %e、%f的默认精度都为6
    • 对大多数的数值类型而言,宽度为输出的最小字符数,如果必要的话会为已格式化的形式填充空格。
    • 对于字符串类型,精度为输出的最大字符数,如果必要的话会直接截断。

二、使用

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
package main

import "fmt"

type Wife struct {
Name string
Age int8
}

type SkillFunc func() string

type Man struct {
Name string
Male bool
Age int8
Addr *string
Salary float32
Hobby []string
Edu map[string]string
Skill SkillFunc
Wife Wife
Other interface{}
}

func main() {
addr := "beijing"

var man = Man{
Name: "liusir",
Male: true,
Age: 18,
Addr: &addr,
Salary: 27800.5,
Hobby: []string{"钓鱼", "跑步", "看电影"},
Edu: map[string]string{"高中":"实验一中", "大学":"北京大学"},
Skill: func() string {
return "I can speak Chinese and a little English"
},
Wife: Wife{Name: "na", Age:18},
Other: 1,
}

skill := func() string {
return "I'm stronger than before"
}
fmt.Printf("%p\n", skill)
skill1 := skill()
fmt.Println(skill1)

skill2 := man.Skill
fmt.Println(skill2)

skill3 := man.Skill()
fmt.Println(skill3)
fmt.Println()

// 普通占位符
fmt.Println("普通占位符=====")
fmt.Printf("%v\n", man)
fmt.Printf("%+v\n", man)
fmt.Printf("%#v\n", man)
fmt.Printf("%T\n", man)
fmt.Printf("%%\n\n")

// 布尔占位符
fmt.Println("布尔占位符=====")
fmt.Printf("%s is male? %t\n\n", man.Name, man.Male)

// 整型占位符
fmt.Println("整型占位符=====")
fmt.Printf("%b\n", 5)
fmt.Printf("%c\n", 0x4E2D)
fmt.Printf("%d\n", 0x12)
fmt.Printf("%o\n", 10)
fmt.Printf("%q\n", 0x4E2D)
fmt.Printf("%x\n", 13)
fmt.Printf("%X\n", 13)
fmt.Printf("%U\n\n", 0x4E2D)

// 浮点数和复数
fmt.Println("浮点数和复数=====")
fmt.Printf("%b\n", 10.2)
fmt.Printf("%e\n", 10.2)
fmt.Printf("%E\n", 10.2)
fmt.Printf("%f\n", 10.2000000)
fmt.Printf("%g\n", 10.20)
fmt.Printf("%G\n\n", 10.2+2i)

// 字符串与字节切片
fmt.Println("字符串与字节切片=====")
fmt.Printf("%s\n", []byte("Go语言"))
fmt.Printf("%q\n", "Go语言")
fmt.Printf("%x\n", "abc")
fmt.Printf("%X\n\n", "abc")

// 指针
fmt.Println("指针=====")
fmt.Printf("%p\n\n", man.Addr)

// 其他
fmt.Println("其他=====")
fmt.Printf("%+q\n", "中文")
fmt.Printf("%#o\n", 10)
fmt.Printf("%#x\n", 10)
fmt.Printf("%#p\n", man.Addr)
fmt.Printf("%#q\n", "Go语言")
fmt.Printf("%#U\n", '中')
fmt.Printf("% d\n", 10)
fmt.Printf("%0d\n", -10)
}

三、参考


Go语言实现枚举

一、基础

二、使用

三、参考

  1. 参考一

Go语言卫述语句

一、基础

二、使用

三、参考