go 快速入门

Created

2024-10-28 13:24:56

Updated

2024-10-28 13:25:01

1 go mod包管理工具

Tip

将github上的一个仓库作为你go项目的一个包来使用

初始化go.mod
# go1.16之前需要
go env -w GO111MODULE=on
# 执行这个命令, 会在当前目录下生成一个 go.mod 文件
go mod init test
查看go.mod
module test

go 1.20
添加一个包
go get github.com/pkg/errors

go get 的时候如果不手动指定版本信息,会自动拉取最新的版本的包

如果想要拉取指定版本可以通过 go get github.com/pkg/errors@v0.9.0 的方式

  • @版本号 例如 @v0.9.0
  • @分支名 例如 @master
  • @commit tag 例如 @6cff360233dc4457f1536e4f3df4e4e740fd3410
再看go.mod文件
module test

go 1.20

require (
    github.com/pkg/errors v0.9.1 // indirect
)

// indirect 表示 这个包我们没有在代码中直接import 使用它, 大概是间接依赖的包, 比如 import A, 然后A里有import B, B就是我们间接依赖的包

package main

import "github.com/pkg/errors"

func main() {
    err := errors.New("abc")
    println(err)
}

当你在main.go 里使用了我们前面go get的包errors, 再去看下 go.mod 会看到提示说 should be direct

# 会下载依赖的包,并清理哪些不再需要的依赖,整理go.mod,
# 这里会将 // indirect 取消掉了,意思就是直接
go mod tidy # 你应该每次下载包,用这个命令来
# 将包缓存到本地 (你项目目录下会生成一个vendor 目录)
go mod vendor
# 使用本地的包来编译, 这样不会去远程拉取
go build -mod vendor
# 仓库作者修改了地址, 你可以修改
# 这样你代码中 import "github.com/Sirupsen/logrus" 实际会使用替换后的
go mod edit -replace  github.com/Sirupsen/logrus@v1.4.1=github.com/sirupsen/logrus@v1.4.1

2 编译运行

main.go
package main

import "fmt"

func main() {
    fmt.Println("hello")
}
简单运行
go run main.go
查看编译过程
go mod init test
go build -n

    # import config
    packagefile fmt=...
    # 一个go程序会将runtime 编译进去.
    packagefile runtime=...
    # 编译
    /usr/local/go/pkg/tool/linux_amd64/compile -o
    # 要link 的文件清单
    cat >$WORK/b001/importcfg.link ...
    # link 操作
    /usr/local/go/pkg/tool/linux_amd64/link -o $WORK/b001/exe/a.out
    /usr/local/go/pkg/tool/linux_amd64/buildid -w $WORK/b001/exe/a.out
    # 重命名
    mv $WORK/b001/exe/a.out test

3 基本数据类型

// 每当遇到const, 就会重置为0
const c1 = iota // 0
const (
    // const() 中每增加一行声明就会将iota计数 +1
    a  = "hello" // hello
    b            // hello
    c  = 9
    d         // 9  使用与它最近的表达式 这里是上面的 9
    e         // 9
    a1 = iota // 5  前面a 那里是0
    a2        //6
    _         //7
    a4        //8
    b1 = iota //9
    b2        // 10
)

const (
    d1 = iota * 2 // 0
    d2            //2
    d3 = iota * 3 // 6
    d4            //9  使用最近的 表达式 iota*3
)

const (
    e1 = 1 << iota //1
    e2             //2  左移 iota=1 位
    e3             //4  左移 iota=2 位
)
const pi=3.14
func main() {
    // # 常量是无法做 取址操作的,会报错
    println(&pi)
}
Tip

const申明的常量,是在编译期间就已经确定的固定值,你无法修改,所以你取址操作无意义

4 空结构体struct

Important

不占用内存, 经常用, 用来节约内存

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    a := struct{}{}
    b := struct{}{}
    fmt.Println(unsafe.Sizeof(a), unsafe.Sizeof(b)) //0
    // 相同
    fmt.Printf("%p,%p\n", &a, &b)
    d := uintptr(unsafe.Pointer(&a))
    fmt.Println(d)

    cat := Cat{
        Name:   "abc",
        Ag:     struct{}{},
        Age:    11,
        Weight: 110,
    }
    // 我们发现 结果是32 , 在结构体里成员类型是空 struct{} ,也是不占用空间的
    fmt.Println(unsafe.Sizeof(cat))
    // 但是 和上面的 a, b 的 空结构体地址不一样.
    // &cat.Ag, &cat.Age 地址一样
    fmt.Printf("%p,%p,%p,%p\n", &cat.Name, &cat.Ag, &cat.Age, &cat.Weight)
    dog := Dog{
        Ag:     struct{}{},
        Name:   "abc",
        Age:    11,
        Weight: 110,
    }
    fmt.Println(unsafe.Sizeof(dog)) // 32 不占用空间
    // &dog.Ag, &dog.Name 地址一样
    fmt.Printf("%p,%p,%p,%p\n", &dog.Ag, &dog.Name, &dog.Age, &dog.Weight)
    // 放在结构体最后
    h1 := struct {
        b byte
        s struct{}
    }{}
    fmt.Printf("%p,%p\n", &h1.b, &h1.s)
    // 2 个字节, 也就是说填充了一个字节
    // 试着 将 b 弄成 int16 int32 int64 看看
    fmt.Println(unsafe.Sizeof(h1))
}
type Cat struct {
    Name   string //16 byte
    Ag     struct{}
    Age    int64 // 8
    Weight int64 // 8
}
type Dog struct {
    Ag     struct{}
    Name   string //16 byte
    Age    int64  // 8
    Weight int64  // 8
}

底层原理
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
    // ....
    if size == 0 {
        // 统一返回全局变量 zerobase 的地址
        return unsafe.Pointer(&zerobase)
    }
}
// base address for all 0-byte allocations
var zerobase uintptr
dlv调试看看
b main.main
c
b 16
c
p d
p uintptr(&runtime.zerobase) # 与上面值一样

5 nil

Tip

nil 是一个变量

源码中,请看看注释
// nil is zero value for a pointer, channel, func, interface, map, or slice type.
// Type must be a pointer, channel, func, interface, map, or slice type
var nil Type
    var a *int
    println(a == nil) //true
    var b chan int
    println(b == nil) //true
    var c func() int
    println(c == nil) //true
    var d interface{}
    println(d == nil) //true
    d = b             // 看看接口的数据结构就知道了
    println(d == nil) //false
    var e map[string]int
    println(e == nil) //true
    var f []int
    println(f == nil) //true

6 make && new

// 只用来创建slice,map和channel
func make(t Type, size ...IntegerType) Type

// 返回的是指针, 指针指向的地址可能在栈上也可能在堆上
func new(Type) *Type
Back to top