记录了mac上go的下载方式和语言特点,并主要讨论了go的基本语法
参考材料:青训营课件,刘丹冰课件(补充),阅读贴:空值零值空结构(juejin.cn/post/689523…
Installation (for Mac):
GO pkg path == /usr/local/go/bin
检查是否下载完成 -> go version 检查版本
go env (检查go环境)
echo $GOPATH (得出 GOPATH="/Users/annadai/go")
cd $GOPATH (进入gopath)里存了src, bin, pkg
Doc: go.dev/doc/install
语言特点
- 直接编译成机器码
- 不依赖其他库,直接运行
- 是静态类型语言: 编译时通过
go build main.go检查隐藏问题 - 能够充分利用多核,通过
go关键词高效实现协程 - 强大标准库:
- runtime 系统调度机制: 垃圾回收,优化
- 高效GC垃圾回收
- 丰富的标准库包
- 内嵌C语法,但目前没办法取代c
- 面向对象程序特征(继承,多态,封装)
- 跨平台运行
Best Applicable Tasks
- 云计算基础设施 e.g. docker
- 后端服务(数据相关的?
基本语法
运行程序
go build hello.go-> 编译; 这里很像c里的gcc hello.c -o hello./hello-> 运行; 这里和c一样,用./executable 运行程序
OR
go run hello.go -> 编译并运行
一个简单的main
package main //这里跟java程序有点像
//import "fmt"
import (
"fmt"
"time"
)
func main(){ //函数名必须和{在同行
fmt.Println("hi!您好!我不好") //;可加可不加
time.Sleep(1 * time.Second)
}
变量Declaration
单个变量
var a int //使用default值
var a int = 100 //设定初始值和dtype
var a = 200 //设定初始值,自动识别dtype
a := 500 //:= 既初始化又赋值,自动匹配dtype
fmt.printf("Type is %T", a) //should be int
BUT! 注意,除了用:=的声明方式,其他都能用在函数外定义全局var
多个变量
var b,c int = 1,2 //如果同一个dtype的话可以加上dtype
var d,e = 5, "hehe" //自动识别dtype
//多行定义
var (
f bool = True
g int = 123
)
constant var
package main
import "fmt"
//枚举
const (
aa = iota * 5 //aa = 0
bb // = 5
cc // = 10
)
const (
a, b = iota+1, iota+2
c, d
e, f
g, h = iota*2, iota*3 //可以换formula,但iota照常累计,所以这里iota = 3
)
func main(){
const x int = 10
//a = iota 这样的写法是不允许的
}
在const()中可以添加iota关键词:第一行默认值为0且每行+1 (iota有点像内置的一个++的for loop)
不同dtype以及零值空值空结构
printf, sprintf 等%v参考:pkg.go.dev/fmt
static type := int, str..
concrete type := interface指向的具体dtype,或者系统看得见的
default值:
值类型:
bool -> false;
数值 -> 0;
str -> "";
array等 -> 递归初始化元素
引用类型(ptr, function, interface, slice, channel, map) -> nil
nil: 可以用来返回错误,但要注意 nil != nil 问题 (使用error接口+不要初始化从函数返回的空错误变量以避免这个问题)
** a := nil 是错误的,因为nil没有type
空结构:如type Q struct{}
特点:0 byte (即使互相套用空结构)
由于不占空间,所以两个不同大小的变量可能有一样的地址
用途:
- 代替
chan bool将传递goroutines的信号表达得更清晰 - 在struct里添加
_ struct{}以防止unkeyed 初始化结构
error
有内置的error package,专门处理error信息
通过errors.New(<error message>)来初始化
func和func返回值的不同写法
func命名方式:大写开头 -> public;小写开头 -> private
parameter: 先名字再dtype
func boo(a string, b int) (int, int){ //匿名return
r1 := 1
r2 := 2
return r1, r2
}
func boo2(a string, b int) (r1 int, r2 int){ //有名称的return,直接return即可
r1 = 100 //已经initialize了dtype,所以不能用:=
r2 = 200
return
//也可以写成return 100, 200
}
//OR
func boo3(a string, b int) (r1, r2 int){
r1 = 100
r2 = 200
return
}
关于import
init函数和import流程
新建folder和相应go程序来创建package
在main里import一个package时,go会先import package里的package (so on if there are more),再执行package里的init -> init()的执行优先级比main()高
*注意import时的路径是基于gopath的
别名和匿名别名
匿名 _ "package1": 无法使用包里的方法,但由于import流程的因素会执行包里的init()
别名 pkg2 "package2": 通过别名.func来调用func (类似python里的import package2 as pkg2)
. "package3" -> 将package3的全部func导入当前程序,调用pkg3里的函数不需要package3作为前缀
(不建议使用,容易引起混淆)
因此,go语言中,如果有import但不使用的package,是会报错的 -> 可以通过起匿名来解决报错
pointer, double pointer (C学过所以不多做笔记)
pass-by-value vs pass-by-reference
& *
defer
defer := func结束之前出发的机制
- 次数不限制
- 触发顺序类似于stack (last in first out)
func main(){
defer func1()
defer func2()
defer func3() //运行顺序为3->2->1
}
- return的执行优先级 > defer
make和new
make用于map, slice, 和channel;但new适用于各种类型的内存分配new会在分配内存之后返回一个ptr- map, slice, 和channel在被
make初始化之前,为nil值(之后也会反复强调)
slice
array vs slice
array :=
- 固定长度
- 不同长度的array是不同的dtype (ex.
[10]int和[3]int是不一样的) - 作为parameter的时候是pass-by-value
var array1 [10]int
array2 := [10]int{1,2,3,4} //除了前四个值都是0
fmt.printf("Type is %T", array1) //should be [10]int
slice :=
- 类似于c里的dynamic array
- 不同长度的slice是同一个dtype
- 作为parameter的时候是pass-by-reference(不是完全的pass-by-value但是会复制ptr的address,所以还是能改变原有值的?)
- 空值为
nil
//四种slice的declaration方式
slice1 := []int{1,2,3,4}
var slice2 []int //空值,不占内存
//slice2[0] = 1 这样写不对,因为slice此时没有任何数
slice2 = make([]int,3) //往slice2里塞三个0
var slice3 []int = make([]int, 3) //初始化并declare slice3有3个空间,都为0
slice4 := make([]int, 3)
Note: make有点类似c里的malloc和calloc,但make仅仅是填充
cap
当目前的已用的空间(长度)不等于容量时,slice内部有个ptr指向已用空间的结束处
var num = make([]int, 3, 5) //5指的是当前最大容量
fmt.Printf("len = %d, cap = %d, slice = %v\n",len(num),cap(num),num) //should be "len = 3, cap = 5, slice = [0,0,0]"
append
注意:当使用append对slice进行追加值的时候,如果长度超过容量,则容量自动*2;但不会对原来的数值或者引用的数值造成影响
num = append(num,1) //往num里面insert一个int1
slicing
跟python的slicing有点像:
s[int a, int b]等同于s[a,b):截取全部char- 默认a为0,默认b为len(s)
- slicing后的var和被slice的var指向同一个address -> 如果改了一个值,两个都会变
copy函数可以拷贝值(不同address的新var)
s := []int{1,2,3}
s1 := s[:,2]
s1[0] = 555 //如果s1变了值,s也会跟着变
//copy函数
s2 := make([]int,3)
copy(s2,s) //将s的值拷贝到s2
map
- 类似于dictionary
- default值为
nil - 使用前必须通过
make分配使用空间,不然报错
3种declaration
var map1 map[string]int //key是str, value是int
map1 = make(map[string]int, 10) //使用前需要给map分配使用空间
map2 := make(map[string]int)
map3 := map[string]int(
"me":1,
"you":2,
"she":3,
)
常见func
map := make(map[string]int)
//add & edit
map["me"] = 1
map["you"] = 2
//delete
delete(map,"me")
//for loop
for key,value := range map{
}