标准库文档
变量声明
//全局变量
var nameTest1 string
var nameTest2 int
var nameTest3 bool
//批量声明,常用
var (
name2 string
age int
ok bool
)
func test(){
//局部变量,局部变量声明必须使用!
var nameTest4="hello"//类型推导
nameTest5 := "world"//简短声明,常用
}
数组定义
数组的长度是数组类型的一部分,确定即不可更改
var array1 [3]bool
array2 := [...]bool{0,1,2,3}//根据初始化自动推断数组长度
array3 := [5]bool{0:1,4:3}//根据索引初始化
多维数组
var array1 [3][4]bool
array1 = [3][4]bool{
[4]bool{true, false, true},
[4]bool{true, false, true},
[4]bool{true, false, true},
}
iota常量计数器
iota
在const关键字出现时将被重置为0
。const中每新增一行常声明,将使iota计数一次(iota可理解为const语句块中的行索引),使用iota能简化定义,在定义枚举时很有用。
package main
import "fmt"
const (
_ = iota
kb = 1 << (10 * iota)
mb = 1 << (10 * iota)
gb = 1 << (10 * iota)
tb = 1 << (10 * iota)
pb = 1 << (10 * iota)
)
func main() {
fmt.Printf("hello Jc!\n")
fmt.Printf("kb is %v\n", kb)
fmt.Printf("mb is %d\n", mb)
fmt.Printf("gb is %d\n", gb)
fmt.Printf("tb is %d\n", tb)
fmt.Printf("pb is %d\n", pb)
}
标准库fmt
格式化输入
var (
string1 string
number1 int
boo1 bool
)
fmt.Scan(&string1)
fmt.Println(string1)
fmt.Scanf("%s %d", &string1, &number1)
fmt.Printf("%s %d\n", string1, number1)
fmt.Scanln(&string1, &number1, &boo1)
fmt.Println(string1, number1, boo1)
格式化输出 打印格式
占位符
字符串
``
用于多行文字保持格式输出,可以用于路径原样输出
string1 := `
多行文字
测试
`
string2 := `D:\Example Code\Go Demo\src\gitee.com\jcgogo`
fmt.Println(string1)
fmt.Println(len(string2))//打印字符串长度
字符串拼接
将两段字符串拼接
string3 := fmt.Sprintf("%s,%s", string1, string2)
字符串分割
将字符串按\
分割为列表
result := strings.Split(string3, "\\")
fmt.Println(result)
字符串包含内容判断
判断字符串中是否包含测试
result2 := strings.Contains(string3, "测试")
fmt.Println(result2)
前后缀判断
判断字符串前缀是否为D
,后缀是否为o
fmt.Println(strings.HasPrefix(string2, "D"))
fmt.Println(strings.HasSuffix(string2, "o"))
字符串子串位置
字符c
的第一个位置和最后一个位置
fmt.Println(strings.Index(string2,"c"))
fmt.Println(strings.LastIndex(string2,"c"))
字符串拼接
将分割好字符串的列表的以~~
拼接
fmt.Println(strings.Join(result1, "~~"))
for遍历对象
for _, v := range string2 {
fmt.Printf("%c ", v)
}
切片(slice)
可变长数组
var slice1 []int //定义了一个int类型的切片(可变长数组)
var slice2 []string //定义了一个string类型的切片
// fmt.Print(slice1 == nil, slice2 == nil, "\n") //未使用赋值则未分配空间
slice1 = []int{1, 2, 3}
slice2 = []string{"jiejie", "Jc"}
// fmt.Print(slice1, slice2)
//长度和容量
fmt.Printf("len(slice1):%d, cap(slice1):%d\n", len(slice1), cap(slice1))
fmt.Printf("len(slice2):%d, cap(slice2):%d\n", len(slice2), cap(slice2))
array1 := [...]int{1, 2, 3, 4, 5}
slice3 := array1[0:3] //从数组中获取切片,左闭右开
fmt.Printf("value:%d,type:%T\n", slice3, slice3)
//切片的容量,是从切片的第一个位置开始,底层数组的容量,长度则为切片的长度
fmt.Printf("len(slice3):%d, cap(slice3):%d\n", len(slice3), cap(slice3))
slice3[0] = 99
fmt.Printf("after change :array1:%d, slice3:%d\n", array1, slice3)
for i, v := range slice3 {
fmt.Println(i, v)
}
切片的特性
切片的长度和容量,本质来说,切片有点c语言里指针的味道,底层数组指针,是引用类型,所以修改切片、或者修改被应用的数组,都会被穿透修改。
- 对切片的修改会穿透到底层数组
- 将切片赋值给其他变量,并对变量进行修改,也会穿透修改
- 切片不能直接比较,除非与nil比较,因为切片为nil意味底层没有引用数组
- 判断切片是否为空应使用len()
- 切片的遍历是数组相同,可以用for range
构造切片
slice4 := make([]string, 5, 10) //make:type,len,cap
fmt.Printf("data:%s,len:%d,cap:%d\n", slice4, len(slice4), cap(slice4))
切片扩容
- 使用append()函数必须使用原来的变量接收
因为底层指向的数组,可能新建在了新的内存块上,指针需要重新指向地址。
slice1 := make([]string, 2, 2)
slice1 = []string{"Jc", "Jiejie"}
fmt.Printf("len:%d,cap:%d\n", len(slice1), cap(slice1))
fmt.Printf("before->data:%s,ptr:%p\n", slice1, &slice1)
slice1 = append(slice1, "hello")
fmt.Printf("len:%d,cap:%d\n", len(slice1), cap(slice1))
fmt.Printf("after->data:%s,ptr:%p\n", slice1, &slice1)
slice2 := []string{"小a", "小b", "小c"}
slice1 = append(slice1, slice2...) //...将slice2拆分
fmt.Printf("len:%d,cap:%d\n", len(slice1), cap(slice1))
fmt.Printf("after->data:%s,ptr:%p\n", slice1, &slice1)
注意:使用...
三个点,可以拆分切片
扩容规则
切片拷贝
要计算好存储对象的空间,否则会截断
slice1 := []string{"Jc", "Jiejie"}
slice2 := []string{"小a", "小b", "小c"}
copy(slice1, slice2) //dst,src
fmt.Printf("%s,%s\n", slice1, slice2)
fmt.Printf("len:%d,cap:%d\n", len(slice1), cap(slice1))
fmt.Printf("len:%d,cap:%d\n", len(slice2), cap(slice2))
切片切除元素
利用append,截断在append
slice2 = append(slice2[:1], slice2[3:]...)
fmt.Printf("%s\n", slice2)
指针
var a int
a = 3
fmt.Printf("value:%d,addr:%p,type:%T\n", *(&a), &a, &a)
make 和 new
make也是用于内存分配的,区别于new,它只用于slice, map以及chan的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。 make函数的函数签名如下:
make 和 new的区别
make 和 new的区别如图
new:
var pointer *int
pointer = new(int)
*pointer = 3
fmt.Printf("%d\n", *pointer)
map
需要先申请空间才能使用,map检测方法如下。
var map1 map[string]string
map1 = make(map[string]string, 1)
map1["type"] = "test"
fmt.Printf("%s\n", map1["type"])
_, ok := map1["miss"]
if !ok {
fmt.Println("miss")
//do something
} else {
//do something
}
for k, v := range map1 {
fmt.Println(k, v)
}
//删除map成员
delete(map1, "type")
map和slice的组合
//声明一个切片,切片指向的类型为map的,key-string,value-string
var slice1 = make([]map[string]string, 5, 5)
slice1[0] = make(map[string]string, 2)
slice1[0]["bot1"] = "test"
slice1[0]["bot2"] = "test"
fmt.Println(slice1[0])
//声明一个map,map存放的类型为切片,key-string,value-slice(int)
var map1 = make(map[string][]int, 5)
map1["slice"] = []int{1, 2, 3}
fmt.Println(map1)
单词出现次数
string1 := "how do you do jc hh hh"
result1 := strings.Split(string1, " ")
stringmap := make(map[string]int, 5)
for _, v := range result1 {
//判断是否有key-v
_, ok := stringmap[v]
//若无,添加到map
if !ok {
stringmap[v] = 1
} else { //若有,计数加1
stringmap[v]++
}
}
fmt.Println(stringmap)
函数
//函数定义,参数类型简写,缺省返回
func add(x, y, z int) (ret int) {
ret = x + y
return
}
//多个返回值
func multi() (int, string) {
return 0, "ok"
}
//可变长参数,必须放在函数参数的最后
func dynValue(x int, y ...int) (ret int) {
fmt.Println(x, y)
return
}
func main() {
j, k := multi()
fmt.Println(j, k)
dynValue(1, 2, 3)
}
defer
延时函数执行,在函数返回前再执行,(函数执行前会将用到的变量先压栈),return非原子操作,defer可能会修改到返回值。
func deferTest() {
fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
fmt.Println(4)
}
func main() {
deferTest()
}
函数作为参数(c语言函数指针)及 匿名函数
示例如下,无限套娃
func deferTest(a int) (string, int) {
fmt.Println(1)
return "", 1
}
//函数接受函数作为参数
func funcRecfunc(a func(int) (string, int)) {
ret := a
fmt.Printf("%T\n", ret)
}
//函数使用函数作为返回值
func funcReturnfunc() func(int) (string, int) {
//匿名函数用法,一般放在函数内部
ret := func(int) (string, int) {
return "nil", 0
}
//匿名函数直接调用
func(int) (string, int) {
return "nil", 0
}(1)
return ret
}
//函数类型作为参数
func main() {
a := deferTest
fmt.Printf("%T\n", a)
funcRecfunc(deferTest)
fmt.Printf("%T\n", funcReturnfunc())
}
闭包
通常外部函数如果预留了接口来拓展其内部功能,那么要使用闭包实现。
//闭包,使得函数能使用外部作用域变量
func diliver(outside int) func(inside int) int {
return func(inside int) int {
inside += outside // 引用到了外层传入的参数
return inside
}
}
//实际应用,对外拓展内部功能。比方Jc写了一个函数eat,并预留了一个extern的函数接口
func eat(extern func()) {
fmt.Println("Jc eat")
extern()
}
//现在Jc学会了study,想边eat边study,但是study比较复杂,不满足extern接口要求
func study(studyWhat string) {
fmt.Println("Jc eating and studying " + studyWhat)
}
//那么使用闭包可以解决
func combineEatStudy(study func(string)) func() {
return func() {
study("golang")
}
}
func main() {
// ret := diliver(5)
// fmt.Printf("%d\n", ret(3))
ret := combineEatStudy(study)
eat(ret)
}
内置函数
new 和 make的区别
panic 和 recover
func a() {
fmt.Println("A Func")
}
func c() {
fmt.Println("C Func")
}
func b() {
panic("B panic")
}
结构体和type
//初始化方式1
var person1 = person{
name: "Jc",
sex: "boy",
}
//初始化方式2,获取到结构体的指针
person4 := &person{
"Jc",
1,
"boy",
}
person1.name = "Jc"
person1.age = 18
person1.sex = "boy"
fmt.Println(person1.name, person1.age, person1.sex)
fmt.Println(person4.name, person4.age, person4.sex)
// 匿名结构体
var person2 struct {
name string
age int
sex string
}
fmt.Println(person2.name, person2.age, person2.sex)
person3 := new(person)
fmt.Println(person3.name, person3.age, person3.sex)