Skip to content

Go 语法

约 1065 个字 178 行代码 预计阅读时间 6 分钟

安装 go

安装 gvm:M1 Mac上更好的 Golang 使用方案:安装、更新、切换 - 知乎 (zhihu.com)

配置 path 路径:macOS搭建GO开发环境 - 知乎 (zhihu.com)

基本概念

零值(nil)

  • nil 表示 pointer、interface、function、map、slice 和 channel 的零值。

循环

for i,v := range a {
    // i 下标
    // v 值
}

for i := 0; i < n; i ++ {

}

包(package)

  • 同一个包下得不同 .go 文件相互之间可以直接引用变量和函数,所以全局变量和函数不能重名。

  • 一个包下只能有一个 main() 函数。

type

// 自定义类型
type MyInt int

// 类型别名
type MyInt64 = int64

自定义类型是定义了新类型,虽然 底层类型 是一样的,存在相互转换的可能。

  • 不会附带原来兴附带的方法

别名,就是 alias , 拥有源类型附带的方法。

接口

接口就是泛型类。

一个接口可以有多个实现类。

package main

import (
    "fmt"
)

type MyInt int
type Myfloat float64

type I interface {
    M()
}

func (v MyInt) M() {
    fmt.Println("Im int")
}

func (v Myfloat) M() {
    fmt.Println("Im float")
}

func main() {
    var i I

    i = MyInt(0)
    i.M()

    i = Myfloat(0.0)
    i.M()
}

常用命令

go run :编译并立即运行代码,不生成可执行文件

go build : 编译代码并生成可执行文件

go install :编译并生成可执行文件,并将可执行文件放到 bin 目录下($GOPATH/bin

指针

package main

import "fmt"

// add2 不修改 main 中的 n,只是一个副本变量
func add2(n int) {
    n += 2
}

// add2ptr 修改指针指向的值
func add2ptr(n *int) {
    *n += 2
}

func main() {
    n := 5
    add2(n)
    fmt.Println(n) // 5
    add2ptr(&n)
    fmt.Println(n) // 7
}

值传递

在 Go 中,函数参数默认采用值传递(pass by value)。这意味着,当你将一个变量传递给函数时,函数接收到的是该变量的一个副本。值传递的含义:

  • 值类型(Value Types):包括基本类型(如 int、float、bool、string 等)、数组、结构体等。传递时会复制整个值。

  • 引用类型(Reference Types):包括切片(slice)、映射(map)、通道(channel)、函数(function)、指针(pointer)等。

  • 传递时会复制引用(如指针),但多个引用会指向相同的底层数据。

  • 切片(Slice):

      > - 引用类型:切片内部包含一个指向底层数组的指针、长度和容量。赋值或传递切片时,会复制这些描述符,但底层数组仍然被共享。
    
      > - 动态长度:切片的长度可以动态变化。
    
      > - ```go
      >     func sliceDemo() {
      >         s := []int{1, 2, 3}
      >         t := s // 浅拷贝,t 和 s 共享底层数组
      >         t[0] = 10
      >         fmt.Println(s) // 输出: [10 2 3]
      >         fmt.Println(t) // 输出: [10 2 3]
      >     }
      >     ```
    

补充概念:

  • 深拷贝 => 拷贝所有的属性,并且地址也与原来的不同,这样的话,你改变当前的属性也不会影响原来的
  • 浅拷贝 => 就是直接赋值的这种,地址相同,当你改变现在的值,原来的值也会跟着改变

闭包

package main

import (
    "fmt"
)

func adder() func(int) int {
    sum := 0
    fmt.Println("当前的 sum = ",sum)
    return func(x int) int {
        sum += x
        fmt.Println(sum,x)
        return sum
    }
}

func main() {
    pos := adder()
    pos(1)
    pos(2)

    adder()
    pos(3)
}

输出:

当前的 sum =  0
1 1
3 2
当前的 sum =  0
6 3

闭包的认识

  • adder 就是为了创造一个环境的,pos := adder() 的时候会分配内存,sum 初始化
  • pos 就是指向的 adder 的返回值,刚好是一个函数。所以调用 pos 函数就是 adder() 的返回内部函数。
  • 共享变量存在环境里面(一块内存空间),每次调用 pos 的时候,变量不会被销毁,闭包结束的时候才被销毁。

方法与指针重定向

参数是强类型的:比较前两个程序,你大概会注意到带指针参数的函数必须接受一个指针:

func ScaleFunc(v *Vertex, f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

v := Vertex{3,4}
ScaleFunc(v, 2)  // 编译错误!
ScaleFunc(&v, 2) // {6,8}

接收者为指针的的方法被调用

  • 接收者既能是值又能是指针
  • 不论调用的是指针还是值,修改原始对象
func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

v := Vertex{3,4}
v.Scale(2)  // {6,8}
p := &v
p.Scale(10) // {60,80}

对于语句 v.Scale(5) 来说,即便 v 是一个值而非指针,带指针接收者的方法也能被直接调用。 也就是说,由于 Scale 方法有一个指针接收者,为方便起见,Go 会将语句 v.Scale(5) 解释为 (&v).Scale(5)

接收者为值的的方法被调用

  • 接收者做的是副本操作,不会修改原始对象,适用于不需要修改接收者的场景。
  • 不论调用的是指针还是值,都不修改原始对象
func (v Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

v := Vertex{3,4}
v.Scale(2)  // v = {3,4}
p := &v
p.Scale(10) // v = {3,4}

函数

func (recv receiver_type) 函数名(parameter_list) (返回值) {

}

内置函数

内置函数 make()new() 都和内存分配有关

  • make 只用于创建并初始化三种内建数据结构:切片(slice)映射(map)通道(channel)
  • new 用 于为任意类型分配 零值 的内存,返回的是指向该类型的指针。

匿名函数

// 变量 fn 接收 func 的函数入口地址
fn := func() {
    sum := 0
    for i := 0; i < 10; i++ {
        sum += i;
    }
    println(sum)
}
fn()

// 便于快速做一些计算
fnc := func(a, b int) int {
    return a + b
}
println(fnc(1, 2))

// 纯匿名函数,只调用一次比较推荐
func() {
    fmt.Printf("hello world!\n")
}() // 最后加括号,表示调用

变参函数

// sum 是一个变参函数,接收任意数量的 int 类型参数
func sum(numbers ...int) int {
    total := 0
    for _, number := range numbers {
        total += number
    }
    return total
}

func main() {
    // 调用变参函数 sum,传入多个参数
    fmt.Println(sum(1, 2, 3))         // 输出: 6
    fmt.Println(sum(4, 5, 6, 7, 8))   // 输出: 30
    fmt.Println(sum())                // 输出: 0
}

map

初始化

在使用 map 之前,必须初始化它。

  • 未初始化的 map 是 nil,对其进行读操作会返回值类型的 零值

  • 对其进行写操作会导致运行时 panic 。

使用 make 直接初始化

m = make(map[string]int)

fmt.Println(m == nil) // 输出: false

// 现在可以安全地进行写入操作
m["k"] = 2
fmt.Println(m["k"]) // 输出: 2

使用 map 字面量声明并初始化 map

var mm = map[string]int{
    "a": 1,
}

fmt.Println(mm["a"]) // 输出: 1
fmt.Println(mm["k"]) // 输出: 0

// 由于 mm 已经被初始化,可以安全地写入新键值对
mm["k"] = 2
fmt.Println(mm["k"]) // 输出: 2

删除一个 key

delete(m,"key")

Last update: February 15, 2025
Created: December 31, 2024

Comments