Golang 基础 Golang basic knowledge
1 变量 variables
1.1 声明变量
Golang 标准的声明变量格式为:
var <name> <type>
其中,var
是关键字,name
是变量名,type
是类型,将var
替换为const
则声明常量.
Go 会对变量进行隐式初始化,比如
string
类型会初始化为空字符串,int
类型就初始化为0,float
就初始化为 0.0,bool
类型就初始化为false,指针类型就初始化为 nil。
在声明过程中,可以对变量初始化
var name string = "vancode golang"
从右值(rvalue)来看,该变量明显是string
类型,因此可以简写为:
var name = "vancode golang"
与python不同,go中双引号表示字符串,而单引号表示rune类型的字符
若右值(rvalue)为浮点型,在不指定类型的情况下,编译器会将你的这个变量声明为 float64,但是很多情况下,我们并不需要这么高的精度(占用的内存空间更大).
故,推荐指定类型:
var rate float32 = 0.89
1.2 多变量声明
声明多个变量可以使用如下语法
var (
name string = "linhao"
age int = 123
)
同样可以使用const
替代var
声明多个常量
const (
NAME string = "LINHAO"
AGE int = 12
)
1.3 推导声明写法
使用:=
声明变量,并对其进行初始化
推导声明写法或者短类型声明法,编译器会自动根据右值类型推断出左值对应的类型.
name := "Vance"
// 等价于
var name string = "Vance"
// 等价于
var name = "Vance"
注意:
该方法只能用于函数内部
func printVar() { name := "Vance" fmt.Println(name) }
1.4 声明和初始化多个变量
name,age := "Vance",24
1.5 new
函数声明一个指针变量
使用new(Type)
将创建一个Type
类型的匿名变量,初始化为Type
类型的0值,然后返回变量地址,返回的指针类型为*Type
.
ptr := new(int)
fmt.Println(ptr)
fmt.Println(*ptr)
new()
是一种语法糖,用new
创建变量和普通变量声明语句方式创建变量没有什么区别,除了不需要声明一个临时变量的名字外,我们还可以在表达式中使用new(Type)
如下两种写法等价
func ptrInt() *int{
return new(int)
}
func ptrIntNew() *int{
var tempInt int
return &tempInt
}
1.6 匿名变量
匿名变量通常用来接受必须接受但是无用的值,用_
表示
func GetData() (int, int) {
return 100, 200
}
func main(){
a, _ := GetData()
_, b := GetData()
fmt.Println(a, b)
}
2 数据类型 The data type
2.1 整型
存储
整形分为有符号整型int
和无符号整型uint
以 8位整型为例
uint
是无符号,表示的是正数,范围为0-255
int
是有符号,表示范围-128-127
- 当你在32位的系统下,
int
和uint
都占用 4个字节,也就是32位 - 若你在64位的系统下,
int
和uint
都占用 8个字节,也就是64位
func SizeIntUnit() {
var intVar int
var uintVar uint
fmt.Printf("intVar 数据类型为 %T,大小为 %d\n", intVar, unsafe.Sizeof(intVar))
fmt.Printf("uintVar 数据类型为 %T,大小为 %d\n", uintVar, unsafe.Sizeof(uintVar))
}
✔️ output
intVar 数据类型为 int,大小为 8
uintVar 数据类型为 uint,大小为 8
进制表示
func PrintBinary() {
num := 0b1100
fmt.Printf("二进制数%b,十进制数%d\n", num, num)
}
func PrintOctal() {
num := 0o1100
fmt.Printf("八进制数%o,十进制数%d\n", num, num)
}
func PrintHexadecimal() {
num := 0x1100
fmt.Printf("十六进制数%X,十进制数%d\n", num, num)
}
fmt
包的格式化输出功能,参考如下
%b 表示为二进制
%c 该值对应的unicode码值
%d 表示为十进制
%o 表示为八进制
%q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
%x 表示为十六进制,使用a-f
%X 表示为十六进制,使用A-F
%U 表示为Unicode格式:U+1234,等价于"U+%04X"
%E 用科学计数法表示
%f 用浮点数表示
2.2 浮点型
Go语言中提供了两种精度的浮点数 float32
和 float64
float32,也即我们常说的单精度,存储占用4个字节,也即4*8=32位,其中1位用来符号,8位用来指数,剩下的23位表示尾数
float64,也即我们熟悉的双精度,存储占用8个字节,也即8*8=64位,其中1位用来符号,11位用来指数,剩下的52位表示尾数
func PrintMax_Min() {
fmt.Printf("MaxFloat32=%E\n", math.MaxFloat32)
fmt.Printf("MaxFloat64=%E\n", math.MaxFloat64)
}
✔️ output
MaxFloat32=3.402823E+38
MaxFloat64=1.797693E+308
2.3 布尔型
Go 语言中,没有布尔型和整型的转换,需要手动转换
func TestBool() {
btrue := true
bfalse := false
fmt.Println()
// bool 转 int
bool2Int := func(b bool) int {
if b {
return 1
}
return 0
}
fmt.Println(bool2Int(btrue))
fmt.Println(bool2Int(bfalse))
// int 转bool
int2Bool := func(i int) bool {
return i != 0
}
fmt.Println(int2Bool(1))
fmt.Println(int2Bool(0))
}
✔️ output
1
0
true
false
2.4 byte rune 和字符串
byte 和 rune
byte
和uint8
类型本质上没有区别,占用1个字节\8位,表示范围0~255,表示的是 ACSII 表中的一个字符rune
和int32
类型本质上没有区别,占用4个字节\32位,表示的是一个 Unicode字符
func PrintByteUint() {
var a byte = 65
var b uint = 66
var a2 byte = 'A'
var b2 uint = 'B'
fmt.Printf("a = %c, b = %c\n", a, b)
fmt.Printf("a2 = %c, b2 = %c\n", a2, b2)
}
✔️ output
a = A, b = B
a2 = A, b2 = B
-
在 Go 中单引号与 双引号并不是等价的,
-
uint8 和 int32 ,直观上让人以为这是一个数值,但是实际上,它也可以表示一个字符,所以为了消除这种直观错觉,就诞生了 byte 和 rune 这两个别名类型。
string
- 字符串使用双引号和反引号表示
- 双引号的字符串带转义字符,而反引号的字符串忽略转义(相当于Python中的raw字符串)
- 双引号字符串,打印时可以使用
fmt
的%q
来忽略转移 - 反引号字符串可以直接在代码中换行来表示字符串换行
func PrintString() {
var str1 string = "hello \\world"
var str2 string = `hello \world`
fmt.Println(str1)
fmt.Println(str2)
fmt.Printf("%q\n", str1)
fmt.Print(`hello
world`)
}
✔️ output
hello \world
hello \world
"hello \\world"
hello
world
2.5 数组
1 声明
Go 语言的数组可以直接按var <name> [<size>]<type>
声明后对单一元素进行赋值,也可以在声明时直接`进行赋值,也可以直接使用赋值对数组声明创建数组变量
func ListStatement() {
// 声明
var arr1 [3]int
// 声明并初始化
var arr2 [3]int = [3]int{1, 2, 3}
// 初始化声明
arr3 := [3]int{1, 2, 3}
fmt.Println(arr1)
fmt.Println(arr2)
fmt.Println(arr3)
}
✔️ output
[0 0 0]
[1 2 3]
[1 2 3]
2 自动分配空间
Go 语言中,数组可以使用...
根据赋值自动分配数组空间
// 数组自动分配空间
func ListAutoSize() {
arr := [...]int{1, 2, 3}
fmt.Println(arr)
fmt.Println(len(arr))
}
✔️ output
[1 2 3]
3
3 数组类型
不同size
的数组属于不同的变量类型
- 可以通过
fmt
的%T
和反射获取变量类型.
// 数组类型
func ListType() {
arr1 := [...]int{1, 2, 3}
arr2 := [...]int{1, 2, 3, 4}
fmt.Println()
// %T 输出变量类型
fmt.Printf("%T\n", arr1)
fmt.Printf("%T\n", arr2)
// 反射输出变量类型
fmt.Println(reflect.TypeOf(arr1))
fmt.Println(reflect.TypeOf(arr2))
}
✔️ output
[3]int
[4]int
[3]int
[4]int
4 类型别名
使用 type
关键字可以定义一个类型字面量,后面只要你想定义一个指定容器,元素类型的数组 ,都可以使用这个别名类型。
func ListTypeAlias() {
type arrType [3]int
myarr := arrType{1, 2, 3}
fmt.Println()
fmt.Println(myarr)
fmt.Println(reflect.TypeOf(myarr))
}
✔️ output
[1 2 3]
list.arrType
5 按索引初始化
在创建时数组时,可以使用指定索引值进行初始化
func ListIndex() {
arr := [4]int{2: 3}
fmt.Println()
fmt.Println(arr)
}
✔️ output
[0 0 3 0]
2.6 切片
切片(Slice)与数组一样,也是可以容纳若干类型相同的元素的容器。与数组不同的是,无法通过切片类型来确定其值的长度。每个切片值都会将数组作为其底层数据结构。我们也把这样的数组称为切片的底层数组。
切片是对数组的一个连续片段的引用,所以切片是一个引用类型,这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内(意思是这是个左闭右开的区间)
关于切片构造,有四种方式:
1 对数组进行片段截取
func SliceCutfromList() {
list := [8]int{1, 2, 3, 4, 5, 6, 7, 8}
// 表示截取数组list的从索引1开始,到索引2(3-1)截至的元素
slice1 := list[1:3]
// 设置切片容量的截止索引
slice2 := list[1:3:4]
fmt.Printf("切片 slice1: %d 的长度为: %d,容量为: %d \n", slice1, len(slice1), cap(slice1))
fmt.Printf("切片 slice2: %d 的长度为: %d,容量为: %d \n", slice2, len(slice2), cap(slice2))
}
✔️ output
切片 slice1: [2 3] 的长度为: 2,容量为: 7
切片 slice2: [2 3] 的长度为: 2,容量为: 3
2 声明并赋值
func SliceStatement() {
var slice1 []int
slice1 = append(slice1, 123)
var slice2 []int = []int{1, 2, 3, 4}
slice3 := []int{1, 2, 3, 4}
fmt.Println()
fmt.Printf("切片 slice1: %d 的长度为: %d,容量为: %d \n", slice1, len(slice1), cap(slice1))
fmt.Printf("切片 slice2: %d 的长度为: %d,容量为: %d \n", slice2, len(slice2), cap(slice2))
fmt.Printf("切片 slice3: %d 的长度为: %d,容量为: %d \n", slice3, len(slice3), cap(slice3))
}
✔️ output
切片 slice1: [123] 的长度为: 1,容量为: 1
切片 slice2: [1 2 3 4] 的长度为: 4,容量为: 4
切片 slice3: [1 2 3 4] 的长度为: 4,容量为: 4
3 使用make
构造切片
使用make
函数构造切片,格式如下:
make( []Type, size, cap )
func SliceMake() {
slice1 := make([]int, 2)
slice2 := make([]int, 2, 6)
fmt.Println()
fmt.Printf("切片 slice1: %d 的长度为: %d,容量为: %d \n", slice1, len(slice1), cap(slice1))
fmt.Printf("切片 slice2: %d 的长度为: %d,容量为: %d \n", slice2, len(slice2), cap(slice2))
}
✔️ output
切片 slice1: [0 0] 的长度为: 2,容量为: 2
切片 slice2: [0 0] 的长度为: 2,容量为: 6
4 指定索引值构造切片
// 指定索引对应值构造
func SliceIndex() {
slice1 := []int{2: 13}
fmt.Println()
fmt.Printf("切片 slice1: %d 的长度为: %d,容量为: %d \n", slice1, len(slice1), cap(slice1))
}
✔️ output
切片 slice1: [0 0 13] 的长度为: 3,容量为: 3
2.7 字典
1 字典声明
以下是三种字典声明方式,注意,Go
中,字典需要指定key
,value
的数据类型.
func DictStatement() {
// 声明并初始化
var dict1 map[string]int = map[string]int{"age": 16, "year": 2022}
dict2 := map[string]int{"age": 16, "year": 2022}
dict3 := make(map[string]int)
dict3["age"] = 16
dict3["year"] = 2022
fmt.Println(dict1)
fmt.Println(dict2)
fmt.Println(dict3)
}
✔️ output
map[age:16 year:2022]
map[age:16 year:2022]
map[age:16 year:2022]
2 字典相关操作
添加\更新元素
使用dict[key]=value
直接修改字典值,如果字典没有该key
则新增元素
// 字典相关操作
func DictOper() {
dict1 := map[string]int{"age": 16, "year": 2022}
// 添加,更新元素
dict1["month"] = 10
dict1["year"] = 2021
fmt.Println()
fmt.Println(dict1)
}
✔️ output
map[age:16 year:2022]
map[age:16 year:2022]
map[age:16 year:2022]
读取元素
如果字典中没有该key
,会返回该数据类型的0值.
fmt.Println(dict1["month"])
fmt.Println(dict1["day"])
✔️ output
10
0
删除元素
删除元素,使用 delete 函数,如果 key 不存在,delete 函数会静默处理,不会报错。
// 删除元素
delete(dict1,"month")
fmt.Println(dict1)
✔️ output
map[age:16 year:2021]
3 字典判断key存在
字典下标读取某key
的值时,不管有没有该key
,都会有返回值(如果没有该key
,返回该数据类型的0值),所以不能靠返回的结果判断有没有key
,但是字典按下标读取时,会有两个返回值,第二个返回值为bool
型表示key
是否存在
// 判断key 存在
func DictKeyExists() {
dict1 := map[string]int{"age": 17, "year": 2022}
checkKeyExists := func(dict map[string]int, key string) {
if value, exist := dict[key]; exist {
fmt.Printf("key: %s 存在,值为: %d\n", key, value)
} else {
fmt.Printf("key: %s 不存在\n", key)
}
}
checkKeyExists(dict1, "month")
checkKeyExists(dict1, "year")
}
✔️ output
key: month 不存在
key: year 存在,值为: 2022
4 字典遍历
Go
中可以使用range
对字典的key
,value
进行遍历
// 字典遍历
func DictRange() {
dict1 := map[string]int{"age": 16, "year": 2022, "month": 2, "day": 1, "hour": 12}
fmt.Println()
for key, value := range dict1 {
fmt.Printf("key:%s,value:%d\n", key, value)
}
for key := range dict1 {
fmt.Printf("key:%s\n", key)
}
for _, value := range dict1 {
fmt.Printf("value:%d\n", value)
}
}
✔️ output
key:month,value:2
key:day,value:1
key:hour,value:12
key:age,value:16
key:year,value:2022
key:age
key:year
key:month
key:day
key:hour
value:12
value:16
value:2022
value:2
value:1