Go 语法¶
约 1065 个字 178 行代码 预计阅读时间 6 分钟
安装 go¶
安装 gvm:M1 Mac上更好的 Golang 使用方案:安装、更新、切换 - 知乎 (zhihu.com)
配置 path 路径:macOS搭建GO开发环境 - 知乎 (zhihu.com)
基本概念¶
零值(nil)
- nil 表示 pointer、interface、function、map、slice 和 channel 的零值。
循环
包(package)
-
同一个包下得不同
.go
文件相互之间可以直接引用变量和函数,所以全局变量和函数不能重名。 -
一个包下只能有一个
main()
函数。
type
自定义类型是定义了新类型,虽然 底层类型 是一样的,存在相互转换的可能。
- 不会附带原来兴附带的方法
别名,就是 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)
}
输出:
闭包的认识
- 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}
函数¶
内置函数
内置函数 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¶
Created: December 31, 2024