P30 回顾,运算符和数组
var [30]int 长度30 int类型的数组
[30]int就代表类型
声明变量做初始化
[…]代表用后面的初始化,让go主机去数长度
数组遍历两种方式,一种是长度,一种是range
有长度为3,每个里面是长度2类型为string的数组,多维数组外层的可以是[…]
go语言里,函数传的是copy拷贝,传的都是值,所以下面函数修改的是副本的值,原来的值是没有修改的
y同样也是拷贝值,不会去修改x
P31 回顾 ,切片
打印一个空其实就是nil,nil在go语言里代表没有分配内存,没有分配内存的切片一定要进行一个初始化操作
初始化操作,两种
声明切片的时候,没有初始化,就没有分配内存,分配之后就一定有内存地址,不是nil了。go偏底层,需要你去申请内存,不需要手动去释放内存。
切片的本质是作为一个引用类型,是不能存值的,底层数组存值。
切片分三部分,指针指的是框框里的第一个内存地址,第二部分长度是切片里包含多少个元素,第三部分容量,当前底层数组能包含多少元素
切片不能直接比较只能和nil比较
切片是引用类型,所以s1和s2指向了相同的地方
切片的扩容策略。
如果申请的容量大于原来的两倍,那就直接扩容至新申请的容量。
如果小于1024,那么就直接两倍;
如果大于1024,就按照1.25倍去扩容。
具体存储的值类型不同,扩容策略也有一定的不同
切片没有初始化是不能直接使用的,append函数自动帮你扩容和初始化
这样初始化,是长度为0,底层数组容量是3,copy的前提是s3有位置可以放下,现在长度是0就等于没位置
现在就有位置了
P32 回顾,指针
*&取地址的符号,取值的符号
*go语言的指针只能读不能修改,string代表字符串类型的指针
根据内存地址找值
需要申请内存,就没有办法直接通过 key找值,没有初始化就等于nil
需要初始化
map判断某个key是否存在,如果对应key不存在,返回的是对应类型的value
删除key,就变成空的了
已经初始化了,就算把key都删除也不等于nil
P33 字符统计
可以判断在指定的字符范围内
下面有一个range表
打印出个数
遍历有几个日文字符
查找单词出现的次数
计算单词的出现次数
这样就计算出了次数
P34 回文判断
会问判断就是一个字符串,从左往右读和从右往左读是一样的
只要遍历一半即可
但是还有问题,现在先造一个rune类型的切片,把字符串追加到切片里,下面遍历切片即可
中文用ascii表示不了,用的utf-8的编码,占三个字节
P35 函数定义和defer
函数就是一段代码的封装
…三个点代表若干个int类型
打印的y就是把数字放在切片里
命名返回值
go语言支持多个返回值
go语言定义函数的时候,函数带命名只能放在函数外面,下面这样是不允许的,在一个命名的函数中不能够再声明命名函数
go里的defer语句会将其后面跟随的语句进行延迟处理,在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序执行,也就是先被defer的语句最后被执行,最后被defer的语句,最先被执行。
加一个defer,代表defer后面的语句延迟到函数即将返回的时候再执行
可以有多个defer语句,打印的顺序就是一个盒子,先放后出,嘿嘿嘿先放进去,最后拿出来。
defer多作用再函数结束前释放资源,文件句柄,数据库链接,socket
绕过函数有返回值,就return返回值,在函数中,return不是原子,是分成两步的,先要把返回值赋值再去return。
如果一个函数中有defer语句,defer语句在return前,执行在返回值赋值之后
go语言中的return不是原子操作,在底层是分为两步执行的:
1.返回值赋值
2.真正的ret返回
函数中如果存在defer,namedefer执行的时机是在第一步和第二步之间
第一个是5,因为修改的是x,return的返回值在这之前已经赋值了
原来的返回值是x,defer在x的基础上+1,再return就是6
函数传参代表copy副本
P36 作用域
函数中变量查找顺序,
1,先在函数内部查找
2.找不到就在函数外面查找,一直找到全局
函数内部定义的变量无法在外部使用
还有if和for作用域
总共三种作用域,全局,函数内部,语句块
P37 函数类型作为参数和返回值
函数也可以作为一种类型存在
这是一个没有参数,没有返回值的函数类型
函数也是有类型区分的,a是没有参数没有返回值的函数类型,b是没有参数有返回值的函数类型
函数可以作为参数的类型,拿到这个函数就执行
有参数的f4就传不进去了
函数还可以做为返回值
返回了一个内存地址
这样就拿到函数结果了
P38 闭包
匿名函数一般用在函数内部,不是下面 一样用在全局的
函数内部没有办法申明有名字的函数,如果只是调用一次的函数,还可以简写成立即执行函数
如果匿名函数有参数,后面的括号就是调用括号,就立即执行函数
典型应用场景,要把类型不匹配的函数传到以前的函数去执行,在f3的 基础上,把f2传进去,既没有参数也没有返回值的东西
lixiang这个匿名函数去调用这个x
参数也可以找外面的
这样调用就是可以把函数传进去,100赋值给m,200给n,
P39 闭包
调用addr函数,得到一个返回值,这个函数就是接收一个int类型参数,返回int结果
闭包,一个函数可以引用外部的变量,ret不是一个简单的函数,x的变量是可以引用外部的变量的。
所以闭包是一个函数,这个函数可以包含外部作用域的一个变量
具体应用就是下面的例子,现在要把f2当作参数传到f1里
底层的原理就是,
1.函数可以作为返回值
2.函数查找变量的顺序,现在自己内部找,找不到就去外层找
ret变量不仅有函数,还有x这个变量
闭包=函数+外部变量的引用,比如f1函数是别人写的,f2函数自己写的,想让别人在f1里执行f2,只能把f2当作参数传到f1
里,f2里有两个参数,可以用第三个函数,用第三个函数接收x,y,在内部返回一个匿名函数,匿名函数没有参数,但是可以闭包的引用找到 x,y
匿名函数间接引用x,y,现在的结果会传到f1
复杂一点,在f3里实现调用f2
ret就是这个匿名函数
就可以把ret这个匿名函数传到f1里
ret是个func类型,就可以直接执行
执行的就是f3里的匿名函数
接收一个string的参数,返回一个函数,这个函数也有一个参数和一个返回值
上面是加上判断什么结尾,不是jpg结尾的都加上jog
函数定义包括,关键字,函数名(参数),返回值
f1是add ,f2是sub
sub和add都用了外面的base,10+1=11,11-2=9
假如前面的不执行,就是13,9
第一次改的是公用的base
P40 defer再讲解
defer就是一个延迟调用,就是为了释放一些资源,defer语句有多条,先放后出
defer执行时机,是放在return语句中返回值赋值和真正return语句中间的位置。
这个修改的x是上面的x
传一个x的指针
一块内存条,上面每个地址可以用16进制表示,a取值符号取出来内存地址,赋值给b
这里执行的时候就已经赋值了,把值传进去了
P41 内置函数
*len可以求字符串长度(求字符串长度,求的是字节长度,一个中文是三个字节),数组长度,切片长度,map。
new 和make都是用来分配内存的,
make是给slice,map,channel,返回还是slice,map,channe
new是来分配值类型的,基本数据类型,int,string,bool,返回对应类型的指针 **
append是用来追加元素的数组、slice
panic和recover是用来做错误处理,错误尝试恢复
go语言设计的时候把错误当作值来处理,每一个错误都有具体的值,不是nil就拿到这个错误进行操作。
panic和recover,当程序出现严重的错误的时候,崩溃了,之前打开的文件还在占用着没有释放,panic和recover就是解决这样的问题了,程序出现问题后,recover可以调用这些释放资源
演示panic释放资源,panic和recover是同时出现的
panic就是崩溃了,退出了
假如在panic之前链接了数据库
recover是尝试恢复
recover会拿到panic的code,进行恢复,让程序不会崩溃退出,recover会去恢复panic状态,但是一般不用
recover必须搭配defer使用,defer一定要在可能引发panic的语句之前定义
P42 fmt标准库介绍
fmt打印有三个系列,print输入什么打印什么,不会换行。
printf,前面的字符串相当于占位符,格式化输出。
println,会自动加换行。
占位符分几个部分,一种是通用占位符。go语言默认输出内容就是%v的。
%+v,类似%v,但输出结构体时会添加字段名。
%#v ,值的GO语法表示
百分号%就是代表特殊的符号,用来接收某个变量。mising代表没传递
两个%号代表真的%号
%t 布尔型,int100不是一个字符串
%b二进制,%U表示unicode格式
65的ascii码应该是A,%q值对应的go字符串字面值
字符传加双引号引号
9.2f总位数9位,小数点 留两位
左对齐还是右对齐
%2.3s,代表总长度2,截取3
获取用户输入,输入变量并赋值
也能拿到值
拼接
P43 homework
分金币
实现这个功能