Go 基本语法 (二)
一. 切片
因为数组的长度是固定的,并且数组的长度属于类型的一部分。所以数组有很多的局限性。所以 go
语言中,提出 切片
的概念
func main() {
var s1 []int // 定义了一个存放 int 类型的切片
var s2 []string //定义了一个存放 string 类型的切片
fmt.Println(s1, s2)
// 初始化
s1 = []int{5, 7, 3}
fmt.Println(s1)
// 长度和容量
fmt.Println(len(s1), cap(s1))
// 结果都是3
// 由数组得到的切片
a1 := [...]int{1, 2, 3, 4, 5, 6}
s3 := a1[0:4] // 从0 切到4
fmt.Println(len(s3), cap(s3))
// 输出结果:4 6, 长度是实际长度,容量是他底层的长度 (从切片的第一个元素到最后元素)
// 切片再切片
s4 := s3[0:2]
fmt.Println(len(s4), cap(s4))
// 切片是一个引用类型,都指向底层的一个数组。
// 输出 (2,6)
}
1.1使用 make 函数创建切片
使用 make 函数,可以指定长度和容量的切片。
make
可以就是用来开辟内存空间的语言
func main() {
s1:=make([]int,5,10)
fmt.Println(s1,len(s1),cap(s1))
// 输出 5,10
}
切片是一个框,相当于框住了一个连续的内存,属于引用类型,真正的数据,是保存在底层的数组里的
判断一个数组是否为空的时候,应该使用 2,而不应该使用 1;
1. fmt.Println(s1==nil)
2. fmt.Println(len(s1)==0)
二.如何为切片动态的追加元素
2.1 错误写法
func main() {
s1 := []string{"北京", "上海", "深圳"}
s1[3] = "广州" // 错误的写法,会发现超出范围
fmt.Println(s1)
}
使用 append
添加
func main() {
s1 := []string{"北京", "上海", "深圳"}
// 调用 append 函数,必须用原来的切片变量接受返回值。
s1 = append(s1, "广州")
fmt.Println(s1)
// 输出
[北京 上海 深圳 广州]
}
使用 append,就是相当于把原来的底层位置换了一个位置。所以要有原来的变量重新接受一下。
func main() {
s1 := []string{"北京", "上海", "深圳"}
s2 := []string{"广州", "武汉", "重庆"}
s1 = append(s1, s2...) // 。。。 在go语言里,是表示把数组拆开
fmt.Println(s1)
}
三. Copy的用法
func main() {
a1 := []int{1, 2, 3}
a2 := a1
var a3 = make([]int, 3)
copy(a3, a1)
fmt.Println(a1, a2, a3)
a1[0] = 100
fmt.Println(a1, a2, a3)
// 输出
[1 2 3] [1 2 3] [1 2 3]
[100 2 3] [100 2 3] [1 2 3]
}
四. 从数组中删除元素
4.1 原本数组中是没有删除元素这一个功能的,所以要删除元素,需要使用元素自带的一些属性
func main() {
a1 := []int{1, 2, 3}
fmt.Println(a1)
// 要删除掉a1 中索引值为 1 的 元素
a1 = append(a1[:1], a1[2:]...)
fmt.Println(a1)
}
4.2使用append 的补充
后面需要的时候再细细的研究吧,现在先记住,使用 append 和使用s1[10]=11,是一样的,只不过他是改了好多个。
func main() {
a1 := [...]int{1, 2, 3, 7, 8, 9, 10, 11}
s1 := a1[:]
// 删除掉索引为1 的底层数组
s1 = append(s1[:1], s1[2:]...)
fmt.Println(s1,len(s1),cap(s1))
fmt.Println(a1,len(a1),cap(a1))
}
// 输出
[1 3 7 8 9 10 11] 7 8
[1 3 7 8 9 10 11 11] 8 8
五.指针
5.1基本操作
go
语言中不需要对指针进行操作,只需要记住两个符号就可以了。
- & :是用来取地址的
- * :用来读地址里的值得
func main() {
a := 10
p := &a
fmt.Println(p)
fmt.Printf("%T\n", p) // 打印一下P的类型
b:=*p
fmt.Println(b)
}
// 输出
0xc000014088
*int (表示的是 int 类型的指针)
10
5.2 指针的错误写法
func main() {
var a *int
*a = 100
fmt.Println(*a)
}
这是因为,声明 var a 的时候,他会默认吧 var 内的指针地址,声明为 nil,因此他里面根本就没有存地址,所以也就无法往里面存值了。
5.3 使用 new 关键字,声明一块地址
func main() {
var a =new(int)
fmt.Println(a)
fmt.Println(*a)
*a=100
fmt.Println(*a)
}
// 输出
0xc000014088
0
100
5.4 使用 make 关键字,声明一块地址
new 和 make 的区别:
- make 和 new 都是用来申请内存的。
- new 很少用,new 一般是用来给基本的类型赋值的,比如
string
int
返回的是 对应类型的指针(*int,*string)。 - make 一般是用来给
slice
map
chan
申请地址的,make 函数返回的是对应类型本身。
六. map
map 是一种无序的,基于 key - value
的数据结构,Go语言中的 map
是引用类型,必须要 初始化才能使用,
6.1 声明的方法:
map[keyType] valueType
6.2 基本的使用
func main() {
var m1 map[string]int
fmt.Println(m1 == nil)
m1 = make(map[string]int, 18) // 应该预估一下他的内存大小,防止二次分配,影响效率
m1["zhao"] = 18
fmt.Println(m1["zhao"])
value, ok := m1["peng"] // 如果要确定是否存在这个人,可以使用该方法
if !ok {
fmt.Println("查无此人")
} else {
fmt.Println(value)
}
}
6.3 遍历 map
func main() {
var m1 map[string]int
fmt.Println(m1 == nil)
m1 = make(map[string]int, 18) // 应该预估一下他的内存大小,防止二次分配,影响效率
m1["zhao"] = 18
m1["zhang"] = 20
// 使用 range 直接遍历
for k, v := range m1 {
fmt.Println(k, v)
}
// 只想得到 key
for k := range m1 {
fmt.Println(k)
}
// 只想得到 value
for _, value := range m1 {
fmt.Println(value)
}
// 删除
delete(m1,"zhang")
fmt.Println(m1)
}
// 输出结果
true
zhang 20
zhao 18
zhao
zhang
18
20
map[zhao:18]
七. 函数
7.1 函数的声明
func Add(x int, y int) (n int) {
return x + y
}
func main() {
r := Add(1, 5)
fmt.Println(r)
}
7.2 函数的一些其他的变形体
// 普通函数
func f1(x int, y int) (n int) {
return x + y
}
// 没有返回值
func f2() {
fmt.Println("f2")
}
// 没有参数,但是有返回值
// 例如返回值,参数可以命名,也可以不命名
func f3() int {
return 5
}
// 命名的返回值
func f7(x, y int) (sum int) {
sum = x + y
return sum
}
// 多个返回值
func f4() (string, int) {
return "青岛", 8
}
// 参数类型简写,当连续多个参数的类型一样的时候,可以简写
func f5(x, y int) {
fmt.Println(x, y)
}
// 可变长参数
// 可变参数一定要写在后边
func f6(x string, y ...int) {
fmt.Println(x)
fmt.Println(y)
}
// go 语言中没有默认参数整个概念
func main() {
r := f1(1, 5)
fmt.Println(r)
f2()
f3()
f4()
f5(5, 8)
f6("zhao", 8, 9, 6, 10, 14, 15)
a := f7(8, 9)
fmt.Println(a)
}
// 输出
6
f2
5 8
zhao
[8 9 6 10 14 15]
17
八. defer 语句
1.defer 语句的一般定义
defer 语句是用于延迟到函数即将返回的时候再执行,一般用于释放某些资源。
func deferDemo() {
fmt.Println("start")
// defer 把他后面的语句延迟到即将返回的时候再执行
// defer 一般是用来释放某些资源的。
defer fmt.Println("嘿嘿")
fmt.Println("哈哈")
}
func main() {
deferDemo()
}
// 输出
start
哈哈
嘿嘿
2.多个 defer 语句的时候
多个defer 语句按照先进后出的顺序延迟执行;
func deferDemo() {
defer fmt.Println("嘿嘿")
defer fmt.Println("哈哈")
defer fmt.Println("呵呵")
}
func main() {
deferDemo()
}
// 输出结果
呵呵
哈哈
嘿嘿
3.defer 执行时机
他不是原子性的执行;
自己写一下:
func f1() int {
x := 5
defer func() {
x++ // 修改的是 x,不是返回值
}()
return x //5
}
// 带命名返回的函数
func f2() (x int) {
defer func() {
x++ // 返回值是x,而x此时=5
}()
return 5 //6
}
func f3() (y int) {
x := 5
defer func() {
x++
}()
return x //5
}
func f4() (x int) {
defer func(x int) {
x++
}(x)
return 5 //5
}
func main() {
a1 := f1()
fmt.Println(a1)
a2 := f2()
fmt.Println(a2)
a3 := f3()
fmt.Println(a3)
a4 := f4()
fmt.Println(a4)
}
九.函数深究
1.函数的查找顺序(作用域)
- 先再函数内部查找
- 找不到就往函数的外面查找,一直找到全局
// 作用域
var x=100
func f1(){
fmt.Println(x)
}
func main() {
f1()
}
2.函数类型
import "fmt"
func f1() {
fmt.Println("Hello,world")
}
func f2() int {
return 6
}
// 函数也可以作为参数类型
func f3(x func() int) {
ret := x()
fmt.Println(ret)
}
// 输入和输出都是函数
func f5(x func() int)func(int,int) int {
rel:=func(a,b,int)
)
}
func main() {
a1 := f1
fmt.Printf("%T", a1)
fmt.Println()
a2 := f2
fmt.Printf("%T", a2)
fmt.Println()
f3(f2)
}
// 输出是 (f5)函数没有输出
func()
func() int
6
在Python里面,函数可以作为参数输入,也可以作为输出的函数,这种叫做高阶函数
3.匿名函数
var f1 = func(x, y int) {
rel := x + y
fmt.Println(rel)
}
func main() {
f1(2, 2)
// 为了解决函数内部没有办法声明带名字的函数
// 匿名函数
f2 := func(x, y int) {
rel := x + y
fmt.Println(rel)
}
f2(2, 8)
// 当作立即执行函数
func() {
fmt.Println("Hellow wrold")
}() // 这个后面加个括号,表示立即执行
func(a, b int) {
fmt.Println(b + a)
}(5, 4) // 后边加括号表示理解执行
}
// 输出
4
Hellow wrold
9
4. 闭包的应用场景
4.1 一些不用闭包不能解决的问题
闭包使用的场景
import "fmt"
func f1(f func()) {
fmt.Println("this is f1")
f()
}
func f2(x, y int) {
fmt.Println("this is f2")
fmt.Println(x + y)
}
func main() {
// 不能应用,因为参数的类型不匹配
f1(f2)
}
4.2使用闭包解决这个问题
闭包:就是一个函数,除了使用他自己内部定义的变量以外,还能使用外边的变量。
func adder(x int) func(int) int{
return func(y int)int{
return x+y
}
}
func main() {
ret:=adder(100)
ret2:=ret(200)
fmt.Println(ret2)
}
// 输出
300
闭包问题,主要是借助这个中间变量
package main
import "fmt"
func f1(f func()) {
fmt.Println("f1执行")
f()
}
func f2(x, y int) {
fmt.Println("f2执行")
fmt.Println(x + y)
}
// 对 f2 进行包装
func middlee(x, y int, f func(x, y int)) func() {
tep := func() {
f(x, y)
}
return tep
}
func main() {
f1(middlee(9, 10, f2))
}
f1执行
f2执行
19