基础知识回顾
String
-
for range遍历 string 得到的是 rune ,通过索引访问string中的元素得到的是byte
// int转string,内部调用了 FormatInt var s string = strconv.Itoa(i) // int64转string 这个10是十进制的意思 s = strconv.FormatInt(i64,10) // string转int i,err = strconv.Atoi(s) // string 转 int64 i64,err = strconv.ParseInt(s,10,64) // 保留两位小数 var f float64 = 8.123456789 s = strconv.FormatFloat(f,'f',2,64) // string 转 float f,err = strconv.ParseFloat(s,64)
格式化输出
输出格式 输出内容
%t 单词true后缀false
%b 表示二进制
%d 表示十进制
%e 有6位小数部分的科学计数法,比如 -1234.456e+78
%f 有6位小数部分,比如 123.456123
%g 根据实际情况采用 %e或者%f格式(获取更简洁、准确的输出)
%s 直接输出字符串或者字节数组
%v 值的默认格式表示
%+v 类似%v,但输出结构体时会添加字段名
%#v 值的Go语法表示
%T 值的类型的Go语法表示
数组
-
数组在声明时必须指定长度,长度不可改变,数组类型包含元累类型和数组长度
-
数组是块连续的内存空间,数组的地址就是首元素的地址
-
var arr2 =[5]int{} var arr3 =[5]intf3,2}//给前2个元素赋值 var arr4 =[5]int{2:15,4:30}//指定index赋值 var arr5 =[..]int{3,2,6,5,4}//根据0里元素的个数推断出数组的长度
切片
type slice struct{ //切片本质是结构体
array unsafe.Pointer //指针
len int //长度
cap int //容量
}
初始化切片
var s []int//切片声明,len=cap=0
s=[]int{} //初始化,len=cap=0
s=make([]int,3)//初始化,len=cap=3
s=make([]int,3,5)//初始化,len=3,cap=5
s=[]int{1,2,3,4,5] //初始化,len=cap=5
截取子切片
arr :=make([]int,3,5) // len=3 cap=5
crr :=arr[0:2] // 截取 0到2 位置
//此时crr 与 arr共享底层内存 也就是说如果crr改变 ,arr也会跟着改变
crr[1] =8
crr = append(crr,4)
crr = append(crr,4) //当超出arr的cap,crr与arr内存分离 这时就 互不影响了
map
· go map的底层实现是hash table,根据key查找value的时间复杂度是O(1) ,但是扩容成本高因为一旦槽位变化,每个值所存放的位置也要变化
// 定义:
// 使用 make() 函数创建一个空的 map
myMap := make(map[keyType]valueType)
// 或者直接声明并初始化 map
myMap := map[keyType]valueType{
key1: value1,
key2: value2,
// ...
}
// 使用 make() 函数创建一个空的 map
myMap := make(map[string]int)
// 或者直接声明并初始化 map
myMap2 := map[string]string{
"name": "John",
"age": "30",
"email": "john@example.com",
}
// 添加元素到 map
myMap["apple"] = 10
myMap["banana"] = 5
myMap["orange"] = 8
// 访问 map 中的元素
fmt.Println("Number of apples:", myMap["apple"])
fmt.Println("Number of oranges:", myMap["orange"])
// 删除 map 中的元素
delete(myMap, "banana")
// 判断某个键是否存在于 map 中
value, ok := myMap["grape"]
if ok {
fmt.Println("Number of grapes:", value)
} else {
fmt.Println("Grape not found.")
}
// 遍历 map
for fruit, count := range myMap {
fmt.Printf("Number of %s: %d\n", fruit, count)
}
遍历引用类型
for i,ele :=range slice{} // 切片
for key,value :=range map{}//不保证遍历的顺序 map
for ele := range ch{} //遍历并取走管道里剩下的元素,一定要先close(ch) channal
for range拿到的是数据的拷贝
结构体和接口
// 结构体
type User struct { // 大写是 包外可见
ld int //大写开头的为导出成员
People //匿名成员,可实现“继承
enrollment time.Time
name,addr string //多个字段类型相同时可以简写到一行里
}
//接口
type Animalinterfacef{
Say(int) (int,error)
}
初始化一个实例
var u User//声明,会用相应类型的默认值初始化struct里的每一个字段
u =User{}//相应类型的默认值初始化struct里的每一个字段
u =&Userf//返回指针
u =User{id:3,name:"zcy"}//赋值初始化
u=User[4,100.0,time.Now(),"zcy","beijing”}//赋值初始化,可以不写字段,但需要跟结构体定义里的字段顺序一致
方法
我们习惯上把结构体的函数称之为方法
//可以把user理解为hello函数的参数,即hello(u user,man string)
func(u User) hello(man string){
fmt.Println("hi" + man + ", my nameis"+u.name)
}
//函数里不需要访问user的成员,可以传匿名,甚至_也不传
func(_ User) think(man string){
fmt.Println("hi"+ man +",do you know my name?")
}
func(u*User) grow(n int) { //如果想修改成员变量,需要传struct的指针
u.age+=n
}
Switch
switch {
case add(5)>10:
fmt.Println("right")
default:
fmt.Println("wrong")
}
switch value := num.(type){//相当于在每个case内部申明了一个变量value。.(type)只能用在switch后面
case int://value已被转换为int类型
fmt.Printf("number is int %dn", value)
case float64: //value已被转换为float64类型
fmt.Printf("number is float64 %fn" value)
case byte,string://如果case后有多个类型,则value还是interface{}类型
fmt.Printf("number is inerface %vn", value)
}
函数传参
传引用类型可以修改底层内存里的值
传数组和结构体会整体拷贝,性能低,且不影响实参。除非传指针
不定长参数
func variableengtharg(a int, other ...int) int {
sum := a
for _,ele:= range other {//不定长参数实际上是slice类型,也就是切片
sum += ele
}
fmt.Printf("len%d cap %d\n",len(other),cap(other))
return sum
}
variable_ength_arg(1)
variable_ength_arg(1,2,3,4)
panic
什么时候会发生panic
1.运行时错误会导致panic,比如数组越界、除0
2.程序自动调用panic(error)
panic会执行什么
1.逆序执行当前的goroutine的defer链(recover从这里开始介入)
2.打印错误信息和调用堆栈
3.调用exit(2)结束整个进程