本内容为本身学习go知识记录的笔记,方便复习查看
笔记内容参考无闻老师的github:https://github.com/Unknwon/go-fundamental-programming
课程视频内容:百度网盘(提取码:mgom)
笔记内容参考:Go编程基础-课堂讲义python
Go是一门 并发支持 、垃圾回收 的 编译型 系统编程语言,旨在创造一门具备在静态编译语言的 高性能 和动态语言的 高效开发 之间拥有良好平衡点的一门编程语言。git
类型安全 和 内存安全
以很是直观和极低代价的方案实现 高并发
高效的垃圾回收机制
快速编译(同时解决C语言中头文件太多的问题)
为多核计算机×××能提高的方案
UTF-8编码支持github
Go在谷歌:以软件工程为目的的语言设计编程
包括VIM,IDEA,Sublime Text,Eclipse等众多知名IDE均已支持数组
全球最大视频网站 Youtube(谷歌)
七牛云存储以及旗下网盘服务(Q盘)
爱好者开发的Go论坛及博客
已用Go开发服务端的著名企业:谷歌、盛大、七牛、360
其它海量开源项目:go-wiki、Go Walker、Go Language Resources七牛云存储
做为一门2009年才正式发布的编程语言,Go是很是年轻的,所以不能称为一门成熟的编程语言,但开发社区天天都在不断更新其核心代码,给咱们这些爱好者给予了很大的学习和开发动力。安全
以Google Group为主的邮件列表天天都会更新10至20帖,国内的Go爱好者QQ群和论坛天天也在进行大量的讨论,所以能够说目前Go爱好者群体是足够壮大。闭包
Go源码安装:参考连接
Go标准包安装:下载地址
第三方工具安装并发
根据约定,GOPATH下须要创建3个目录:
bin(存放编译后生成的可执行文件)
pkg(存放编译后生成的包文件)
src(存放项目源码)app
在命令行或终端输入go便可查看全部支持的命令
go get:获取远程包(需 提早安装 git或hg) go run:直接运行程序 go build:测试编译,检查是否有编译错误 go fmt:格式化源码(部分IDE在保存时自动调用) go install:编译包文件并编译整个程序 go test:运行测试文件 go doc:查看文档(CHM手册)
本套教程主要使用 Sublime Text
其它IDE安装方案:参考连接
Sublime Text
下载Sublime Text:官方网站
安装gosublime(破解版可能没法安装):安装指令
Sublime Text 2 入门及技巧
输出:hello.go
- break default func interface select - case defer go map struct - chan else goto package switch - const fallthrough if range type - continue for import return var
// 单行注释 /* */ 多行注释
Go程序是经过 package 来组织的(与python相似)
只有 package 名称为 main 的包能够包含 main 函数
一个可执行程序 有且仅有 一个 main 包
经过 import 关键字来导入其它非 main 包
经过 const 关键字来进行常量的定义
经过在函数体外部使用 var 关键字来进行全局变量的声明与赋值
经过 type 关键字来进行结构(struct)或接口(interface)的声明
经过 func 关键字来进行函数的声明
导入包以后,就可使用格式<PackageName>.<FuncName>
来对包中的函数进行调用
若是导入包以后 未调用 其中的函数或者类型将会报出编译错误:
当使用第三方包时,包名可能会很是接近或者相同,此时就可使用
别名来进行区别和调用
不建议使用,易混淆
不能够和别名同时使用
Go语言中,使用 大小写 来决定该 常量、变量、类型、接口、结构
或函数 是否能够被外部包所调用:
根据约定,函数名首字母 小写 即为private
函数名首字母 大写 即为public
或通常类型(非接口、非结构)是否也能够用一样的方法呢?
布尔型:bool
type (
bype int8
rune int32
文本 string
)
func main() {
var b 文本
b = "中文类型名字"
fmt.Println(b)
}
$ go run basic_struct.go
中文类型名字
变量的声明格式:var <变量名称> <变量类型>
变量的赋值格式:<变量名称> = <表达式>
声明的同时赋值:var <变量名称> [变量类型] = <表达式>
Go中不存在隐式转换,全部类型转换必须显式声明
转换只能发生在两种相互兼容的类型之间
类型转换的格式:
package main import ( "fmt" ) func main() { //fmt.Println("hello world") var a int = 65 fmt.Println(a) b := string(a) fmt.Println(b) } daixuandeMacBook-Pro:学习go daixuan$ go run hello.go 65 A 若是我就是想输出字符串65,怎么办? package main import ( "fmt" "strconv" ) func main() { var a int = 65 b := strconv.Itoa(a) fmt.Println(b) } $ go run basic_struct.go 65
string() 表示将数据转换成文本格式,由于计算机中存储的任何东西
本质上都是数字,所以此函数天然地认为咱们须要的是用数字65表示
的文本 A。
常量的值在编译时就已经肯定
常量的定义格式与变量基本相同
等号右侧必须是常量或者常量表达式
常量表达式中的函数必须是内置函数
在定义常量组时,若是不提供初始值,则表示将使用上行的表达式
使用相同的表达式不表明具备相同的值
package main import ( "fmt" ) const ( a, b = 1, "2" c, d //这里两个变量c,d使用初始化规则,值等于上一行表达式,注意每一行的常量个数相同 ) func main() { fmt.Println(a) fmt.Println(b) fmt.Println(c) fmt.Println(d) } $ go run basic_struct.go 1 2 1 2
iota是常量的计数器,从0开始,组中每定义1个常量自动递增1
经过初始化规则与iota能够达到枚举的效果
每遇到一个const关键字,iota就会重置为0
package main import ( "fmt" ) const ( a = "A" b //b初始化规则b=a="A" c = iota //已经有2个常量a,b,因此c=2 d //注意:这里d套用了c的常量表达式d=iota,已经有3个常量a,b,c,因此d=3 ) const ( e = iota //每遇到一个const关键字,iota就会重置为0,因此e=iota=0 f //f套用了e的常量表达式f=iota,e=0,f=e+1=1 ) func main() { fmt.Println(a) fmt.Println(b) fmt.Println(c) fmt.Println(d) fmt.Println(e) fmt.Println(f) } go run basic_struct.go A A 2 3 0 1
Go中的运算符均是从左至右结合
优先级(从高到低)
^ ! (一元运算符)
^(异或,相同为0,不一样为1) (二元运算符) == != < <= >= > <- (专门用于channel) && |
---|
package main import ( "fmt" ) /* 6 :0110 11:1011 & 0010 = 2 | 1111 =15 ^ 1101 =13 //异或,相同为0,不一样为1,1^0=1,0^1=1,0^0=0,1^1=0 &^ 0100 = 4 //表示若是B后边的为1,须要强制将A的改成0 */ func main() { fmt.Println(6 & 11) fmt.Println(6 | 11) fmt.Println(6 ^ 11) fmt.Println(6 &^ 11) } go run basic_struct.go 2 15 13 4
Go虽然保留了指针,但与其它编程语言不一样的是,在Go当中不
支持指针运算以及”->”运算符,而直接采用”.”选择符来操做指针
目标对象的成员
操做符”&”取变量地址,使用”*”经过指针间接访问目标对象
默认值为 nil 而非 NULL
递增递减语句
func main() { a := 1 var p *int = &a //p是指向a地址(0xc420016088)的int指针 fmt.Println(p) fmt.Println(*p) } $go run basic_struct.go 0xc420016088 1 *p取地址内的值1 func main() { a := 1 var p = &a fmt.Println(p) fmt.Println(*p) } $ go run basic_struct.go 0xc420016088 1
在Go当中,++ 与 -- 是做为语句而并非做为表达式
简单理解为:
/容许: a :=1 a++ //不容许,报错 a :=1 a := a++
条件表达式没有括号
支持一个初始化表达式(能够是并行方式)
左大括号必须和条件语句或else在同一行
支持单行模式
初始化语句中的变量为block级别,同时隐藏外部同名变量1.0.3版本中的编译器BUG
func main() { a := 10 if a := 3; a > 1 { //能够在if中初始化a:=3,可是a=3仅仅在if中有效 fmt.Println(a) } fmt.Println(a) //若是没有提早定义a :=10 ,这里会报错 } go run basic_struct.go 3 10
func main() { a := true if a, b, c := 1, 2, 3; a+b+c > 6 { //能够在if中初始化a:=1,可是a=1仅仅在if中有效,if外a=true fmt.Println("大于6") } else { fmt.Println("小于等于6") fmt.Println(a) } fmt.Println(a) } $ go run basic_struct.go 小于等于6 1 true
Go只有for一个循环语句关键字,但支持3种形式
初始化和步进表达式能够是多个值
条件语句每次循环都会被从新检查,所以不建议在条件语句中
使用函数,尽可能提早计算好条件并以变量或常量代替
左大括号必须和条件语句在同一行
(1)for+if
func main() { a := 1 for { a++ if a > 3 { break } fmt.Println(a) } fmt.Println("Over") } $ go run basic_struct.go 2 3 Over
(2)for 自带判断条件
func main() { a := 1 for a <= 3 { a++ fmt.Println(a) } fmt.Println("Over") } $ go run basic_struct.go 2 3 4 Over
(3经典方式
func main() { a := 1 for i := 0; i < 3; i++ { a++ fmt.Println(a) } fmt.Println("Over") } $ go run basic_struct.go 2 3 4 Over
可使用任何类型或表达式做为条件语句
不须要写break,一旦条件符合自动终止
如但愿继续执行下一个case,需使用fallthrough语句
支持一个初始化表达式(能够是并行方式),右侧需跟分号
左大括号必须和条件语句在同一行
func main() { a := 1 switch a { case 0: fmt.Println("a=0") case 1: fmt.Println("a=1") default: fmt.Println("None") } } $ go run basic_struct.go a=1 func main() { a := 1 switch { case a >= 0: fmt.Println("a=0") //a=0知足条件,打印a=0跳出 case a >= 1: fmt.Println("a=1") default: fmt.Println("None") } } $ go run basic_struct.go a=0 func main() { a := 1 switch { case a >= 0: fmt.Println("a=0") //a=0知足条件,打印a=0,有fallthrough不跳出,检查下一个 fallthrough case a >= 1: fmt.Println("a=1") default: fmt.Println("None") } } $ go run basic_struct.go a=0 a=1 func main() { switch a := 1; { //在switch中定义a,做用于switch内部,外部调用失败 case a >= 0: fmt.Println("a=0") fallthrough case a >= 1: fmt.Println("a=1") default: fmt.Println("None") } } $ go run basic_struct.go a=0 a=1
三个语法均可以配合标签使用,实现跳出多层循环
标签名区分大小写,若不使用会形成编译错误
Break与continue配合标签可用于多层循环的跳出
Goto是调整执行位置,与其它2个语句配合标签的结果并不相同
func main() { LABEL1: for { for i := 0; i < 10; i++ { if i > 3 { break LABEL1 } } } fmt.Println("ok") } $ go run basic_struct.go ok func main() { for { for i := 0; i < 10; i++ { if i > 3 { goto LABEL1 } } } LABEL1: //标签放在最外层循环的外侧,确保不死循环,能够跳出 fmt.Println("ok") } $ go run basic_struct.go ok func main() { LABEL1: for i := 0; i < 10; i++ { //把有限循环放在最外面,那么continue最终会结束,不会死循环 for { continue LABEL1 fmt.Println(i) } } fmt.Println("ok") } $ go run basic_struct.go ok
请尝试并思考为何。
func main() { LABEL1: for i := 0; i < 10; i++ { for { fmt.Println(i) continue LABEL1 } } fmt.Println("ok") } $ go run basic_struct.go 0 1 2 3 4 5 6 7 8 9 ok func main() { LABEL1: for i := 0; i < 10; i++ { for { fmt.Println(i) goto LABEL1 } } fmt.Println("ok") } $ go run basic_struct.go 0 0 0 ...... 由于一执行循环,输出0后,goto到label1,用从新开始循环,从新输出0以后,又调到label1,又进入循环,无线下去输出0,死循环
定义数组的格式:var <varName> [n]<type>,n>=0
数组长度也是类型的一部分,所以具备不一样长度的数组为不一样类型
func main() { var a [2]int //初始化int类型数组a,定义为2个元素,默认值为0 var b [2]int b = a fmt.Println(b) } $ go run basic_struct.go [0 0] func main() { a := [2]int{1, 1} //初始化int类型数组a,定义为2个元素,初始值为1 var b [2]int b = a fmt.Println(b) } $ go run basic_struct.go [1 1] func main() { a := [2]int{1} //初始化int类型数组a,定义为2个元素,初始值一、0 var b [2]int b = a fmt.Println(b) } $ go run basic_struct.go [1 0] func main() { a := [20]int{18: 2, 19: 1} //定义数组第19个元素值为2,第20个元素为1 fmt.Println(a) } $ go run basic_struct.go [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1] func main() { a := [...]int{3, 2, 1, 19: 1} //使用...定义数组,第20个元素是1,则数组长度为20 fmt.Println(a) } $ go run basic_struct.go [3 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
注意区分:
指向数组的指针[100]int -->&[0 0 0 0 0 0 0 0 0 1]
指针数组[100]int -->[0xc420016088 0xc420016090]
func main() { a := [...]int{9: 1} var p *[10]int = &a //p是指向数组的指针 fmt.Println(p) } $ go run basic_struct.go &[0 0 0 0 0 0 0 0 0 1] //数组前面有一个&,p是指向数组的指针 func main() { a := [...]int{9: 1} var p = &a //这样更简单,p是指向数组的指针 fmt.Println(p) } $ go run basic_struct.go &[0 0 0 0 0 0 0 0 0 1] func main() { x, y := 1, 2 a := [...]*int{&x, &y} //定义a是指针类型的数组,保存的数组元素是指向int型的指针:变量x和y的地址 fmt.Println(a) } $ go run basic_struct.go [0xc420016088 0xc420016090]
数组在Go中为值类型,传递数组是整个拷贝的,其余语言为了节省内存,数组是引用类型。
数组之间可使用==或!=进行比较,但不可使用<或>
func main() { a := [2]int{1, 2} b := [2]int{1, 2} fmt.Println(a == b) } $ go run basic_struct.go true func main() { a := [2]int{1, 2} b := [2]int{1, 200} fmt.Println(a == b) } $ go run basic_struct.go false //注意:数组长度不一样,没法直接比较,不然报错 func main() { a := [2]int{1, 2} b := [1]int{10} fmt.Println(a == b) } $ go run basic_struct.go # command-line-arguments ./basic_struct.go:17:16: invalid operation: a == b (mismatched types [2]int and [1]int)
可使用new来建立数组,此方法返回一个指向数组的指针
func main() { p := new([10]int) fmt.Println(p) } $ go run basic_struct.go &[0 0 0 0 0 0 0 0 0 0] //可使用索引直接对数组元素操做 func main() { a := [10]int{} a[1] = 2 //使用索引直接对数组元素赋值 fmt.Println(a) p := new([10]int) p[1] = 2 //使用索引直接对数组元素赋值 fmt.Println(p) fmt.Println(*p) } $ go run basic_struct.go [0 2 0 0 0 0 0 0 0 0] &[0 2 0 0 0 0 0 0 0 0] [0 2 0 0 0 0 0 0 0 0]
Go支持多维数组
func main() { a := [2][3]int{ {1, 1, 1}, {2, 2, 2}} fmt.Println(a) } $ go run basic_struct.go [[1 1 1] [2 2 2]] func main() { a := [2][3]int{ {1: 1}, //给数组元素赋值 {2: 2}} //给数组元素赋值 fmt.Println(a) } $ go run basic_struct.go [[0 1 0] [0 0 2]]
Go语言版冒泡排序
func main() { a := []int{5, 2, 6, 3, 9} fmt.Println(a) num := len(a) for i := 0; i < num; i++ { for j := i + 1; j < num; j++ { if a[i] < a[j] { temp := a[i] a[i] = a[j] a[j] = temp } } } fmt.Println(a) } $ go run basic_struct.go [5 2 6 3 9] [9 6 5 3 2]
其自己并非数组,它指向底层的数组
做为变长数组的替代方案,能够关联底层数组的局部或所有
为引用类型
能够直接建立或从底层数组获取生成
使用len()获取元素个数,cap()获取容量
通常使用make()建立
若是多个slice指向相同底层数组,其中一个的值改变会影响所有
func main() { a := [10]int{1,2,3,4,5,6,7,8,9,0} fmt.Println(a) s1:=a[5:10]//a[5:10]指的是:a[5,6,7,8,9],不包括a[10] fmt.Println(s1) } $ go run basic_struct.go [1 2 3 4 5 6 7 8 9 0] [6 7 8 9 0] func main() { a := [10]int{1,2,3,4,5,6,7,8,9,0} fmt.Println(a) s1:=a[5:len(a)]//a[5,6,7,8,9] fmt.Println(s1) } $ go run basic_struct.go [1 2 3 4 5 6 7 8 9 0] [6 7 8 9 0] func main() { a := [10]int{1,2,3,4,5,6,7,8,9,0} fmt.Println(a) s1:=a[5:]//a[5,6,7,8,9] fmt.Println(s1) } $ go run basic_struct.go [1 2 3 4 5 6 7 8 9 0] [6 7 8 9 0] func main() { a := [10]int{1,2,3,4,5,6,7,8,9,0} fmt.Println(a) s1:=a[:5]//a[5,6,7,8,9] fmt.Println(s1) } $ go run basic_struct.go [1 2 3 4 5 6 7 8 9 0] [1 2 3 4 5]
make([]T, len, cap)
其中cap能够省略,则和len的值相同
len表示存数的元素个数,cap表示容量
func main() { s1:=make([]int,3,10) fmt.Println(s1) } $ go run basic_struct.go [0 0 0] func main() { s1:=make([]int,3,10) fmt.Println(s1) fmt.Println(len(s1),cap(s1))//打印元素个数3和容量10 } $ go run basic_struct.go [0 0 0] 3 10
func main() { a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'} sa := a[2:5] fmt.Println(string(sa)) } $ go run basic_struct.go cde func main() { a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'} sb := a[3:5] fmt.Println(string(sb)) } $ go run basic_struct.go de
Reslice时索引以被slice的切片为准
索引不能够超过被slice的切片的容量cap()值
索引越界不会致使底层数组的从新分配而是引起错误
func main() { a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'} sa := a[2:5] fmt.Println(len(sa),cap(sa))//sa元素3,容量是9 sb := a[3:5] fmt.Println(len(sb),cap(sb))//sb元素2,容量是8,比sa少1 fmt.Println(string(sa)) fmt.Println(string(sb)) } $ go run basic_struct.go 3 9 2 8 cde de func main() { a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'} sa := a[2:5] fmt.Println(len(sa),cap(sa))//sa元素3,容量是9(cdefghijk) sb := sa[2:5] fmt.Println(len(sb),cap(sb))//sb元素3,容量是7(efghijk) fmt.Println(string(sa)) fmt.Println(string(sb)) } $ go run basic_struct.go 3 9 3 7 cde efg
能够在slice尾部追加元素
能够将一个slice追加在另外一个slice尾部
若是最终长度未超过追加到slice的容量则返回原始slice
若是超过追加到的slice的容量则将从新分配数组并拷贝原始数据(容量翻倍)
func main() { s1 := make([]int,3,6) fmt.Printf("%p\n",s1) s1 = append(s1,1,2,3) fmt.Printf("%v,%p\n",s1,s1) } $ go run basic_struct.go 0xc4200180c0 //容量没有变化,内存地址没有变化 [0 0 0 1 2 3] 0xc4200180c0 func main() { s1 := make([]int,3,6) fmt.Printf("%p\n",s1) s1 = append(s1,1,2,3) fmt.Printf("%v,%p\n",s1,s1) s1 = append(s1,1,2,3) //这里容量不够,从新分配容量(翻倍),拷贝原来元素再追加 fmt.Printf("%v,%p\n",s1,s1) } $ go run basic_struct.go 0xc420092000 [0 0 0 1 2 3],0xc420092000 [0 0 0 1 2 3 1 2 3],0xc420072060 func main() { a := []int{1,2,3,4,5} s1 := a[2:5] s2 := a[1:3] fmt.Println(s1,s2) s1[0] = 9 //slice指向底层数组,多个slice指向同一个数组时候,其中一个改变,会影响数组,同时影响其余slice值 fmt.Println(a) fmt.Println(s1,s2) } $ go run basic_struct.go [3 4 5] [2 3] [1 2 9 4 5] [9 4 5] [2 9] func main() { a := []int{1,2,3,4,5} s1 := a[2:5] s2 := a[1:3] fmt.Println(s1,s2) s2=append(s2,1,2,1,1,1,1,1,1,1)//apend元素个数超过slice容量,会指向新的内存地址的底层数组(从a拷贝过来的),此时改变a,不会影响s2的值 s1[0] = 9 fmt.Println(a) fmt.Println(s1) fmt.Println(s2) } $ go run basic_struct.go [3 4 5] [2 3] [1 2 9 4 5] [9 4 5] [2 3 1 2 1 1 1 1 1 1 1]
slice拷贝,以个数少的为准
//把s2拷贝到s1 func main() { s1:=[]int{1,2,3,4,5,6} s2:=[]int{7,8,9} copy(s1,s2) //s2的元素7,8,9会覆盖s1的前三个元素1,2,3 fmt.Println(s1) } $ go run basic_struct.go [7 8 9 4 5 6] //把s1拷贝到s2 func main() { s1:=[]int{1,2,3,4,5,6} s2:=[]int{7,8,9} copy(s2,s1)//把s1的前三个元素1,2,3拷贝覆盖s2 fmt.Println(s2,s1) } $ go run basic_struct.go [1 2 3] [1 2 3 4 5 6] func main() { s1:=[]int{1,2,3,4,5,6} s2:=[]int{7,8,9} copy(s2,s1[3:6]) //把s1的后三个元素拷贝到s2,覆盖s2元素 fmt.Println(s2,s1) } $ go run basic_struct.go [4 5 6] [1 2 3 4 5 6] func main() { s1:=[]int{1,2,3,4,5,6} s2:=[]int{7,8,9} copy(s2[1:3],s1[4:6]) //把s1的后2个元素拷贝到s2的后两位,覆盖s2后2个元素,保留第一个元素7 fmt.Println(s2,s1) } $ go run basic_struct.go [7 5 6] [1 2 3 4 5 6]
相似其它语言中的哈希表或者字典,以key-value形式存储数据
Key必须是支持==或!=比较运算的类型,不能够是函数、map或slice
Map查找比线性搜索快不少,但比使用索引访问数据的类型慢100倍
Map使用make()建立,支持 := 这种简写方式
func main() { var m map[int]string //定义map,int是key类型,string是value类型 m=map[int]string{} fmt.Println(m) } $ go run basic_struct.go map[]
make([keyType]valueType, cap),cap表示容量,可省略
超出容量时会自动扩容,但尽可能提供一个合理的初始值
使用len()获取元素个数
func main() { var m map[int]string=make(map[int]string) fmt.Println(m) } $ go run basic_struct.go map[] func main() { var m =make(map[int]string) fmt.Println(m) } $ go run basic_struct.go map[] func main() { m :=make(map[int]string) fmt.Println(m) } $ go run basic_struct.go map[] func main() { m :=make(map[int]string) m[1] = "ok" fmt.Println(m) } $ go run basic_struct.go map[1:ok] func main() { m :=make(map[int]string) m[1] = "ok" a:=m[1] fmt.Println(a) } ok func main() { m :=make(map[int]string) //m[1] = "ok" a:=m[1] fmt.Println(a) } 输出为空
键值对不存在时自动添加,使用delete()删除某键值对
使用 for range 对map和slice进行迭代操做
func main() { m :=make(map[int]string) m[1] = "ok" delete(m,1) a:=m[1] fmt.Println(a) } 输出为空 func main() { m := make(map[int]map[int]string) //使用make建立初始化m,定义m的value是另外一个map[int]string m[1]=make(map[int]string)//这里是用make初始化另外一个map,做为value赋值给m[1] m[1][1]= "ok" a:=m[1][1] fmt.Println(a) } ok func main() { m := make(map[int]map[int]string) m[1]=make(map[int]string) //这里只对m[1]初始化了,没有对m[2]初始化,因此把ok赋值给nil报错 m[2][1]= "ok" b:=m[2][1] fmt.Println(b) } panic: assignment to entry in nil map goroutine 1 [running]: main.main() /Users/daixuan/go/hello.go:8 +0x168
map嵌套map需注意,每一级的map都得初始化,怎么知道map是否被初始化呢?
采用多返回值的方式,当有一个返回值的时候,返回value,当有多个返回值的时候,第二个返回bool类型的值,true或者false
package main import "fmt" func main() { m := make(map[int]map[int]string) m[1]=make(map[int]string) m[1][1]= "123" a, ok :=m[1][1] //m[1]被初始化,a=123,不为空,因此ok=true b, nok :=m[2][1] //m[2]未被初始化,b=nil,为空,因此nok=false fmt.Println(a,ok) fmt.Println(b,nok) } 123 true false package main import "fmt" func main() { m := make(map[int]map[int]string) a, ok :=m[2][1] fmt.Println(a,ok) //第一次没有初始化map,因此ok=false if !ok { //这里判断是否ok==false,那么去初始化map[2](第二级的map) m[2]=make(map[int]string)//初始化第二级别map } m[2][1]="good"//赋值给第二级map的key=1的value是'good' a, ok =m[2][1] fmt.Println(a,ok) //a=good,因此ok=true } false good true
迭代操做(slice和map均可以迭代操做):
package main import ( "fmt" ) func main() { slice := []int{10,20,30,40} for index,value :=range slice { //i是slice的索引,至关于计数器,int型,0,1,2,3,4....,v是slice存储的值取出赋值给v,修改v的值不会影响slice自己 if slice[index]==30{ slice[index]=300 //使用slice[index]=300 能够直接修改slice原始的值为300 value=400 fmt.Println(index,value) fmt.Println(slice[index]) //使用slice[index]=300 能够直接修改slice原始的值为300 } } } 2 400 300 package main import ( "fmt" ) func main() { slice := []int{10,20,30,40} for _,value :=range slice { //用空白标识符下划线 _ 来忽略索引值 fmt.Println(value) } } 10 20 30 40 package main import ( "fmt" ) func main() { m := make(map[int]string) m[1]="ok" for key, value := range m { fmt.Println(key, value) } info := map[string]string{ "name": "david", "address": "shanghai", } for k,v := range info{ fmt.Printf("Key:%s,Value:%s\n",k,v) } } 1 ok Key:name,Value:david Key:address,Value:shanghai package main import ( "fmt" ) func main() { sm := make([]map[int]string,5)//定义一个以map为原数类型的slice,定义map方法:m :=map[int]string,定义slice方法:slice := make([]string,5) for _, v := range sm { //对slice:sm进程迭代操做 v = make(map[int]string) //对slice中的map初始化 v[1] ="OK" //这里对v是个拷贝赋值,不会影响slice自己的值,因此v=map[1:OK],而sm是:[map[] map[] map[] map[] map[]] fmt.Println(v) } fmt.Println(sm) } map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK] [map[] map[] map[] map[] map[]]
若是想把slice的值修改掉,怎么办呢?
package main import ( "fmt" ) func main() { sm := make([]map[int]string,5)//定义一个以map为原数类型的slice,定义map方法:m :=map[int]string,定义slice方法:slice := make([]string,5) for i := range sm { //对slice:sm进程迭代操做,i=0,1,2,3,4 sm[i] = make(map[int]string) //对slice中的map初始化 sm[i][1] ="OK" //这里对第i个切片sm[i]赋值key=1,value="OK",会影响slice自己的值,因此v=map[1:OK],而sm是:[map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK]] fmt.Println(sm[i]) } fmt.Println(sm) } map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK] [map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK]]
map是无序的,不能直接排序,可是咱们能够对其key进程间接排序,须要借助slice,实现根据key有序的取出map中的值,实现map的简介排序
package main import ( "fmt" ) func main() { m := map[int]string{1:"a",2:"b",3:"c",4:"d",5:"e"} //定义map,map没有索引 s := make([]int,len(m))//定义slice,slice以索引为固定的key i :=0 for k,_ := range m{ s[i] = k //把map中全部的key存在slice中,可是无序的 i++ } fmt.Println(s) } [5 1 2 3 4] package main import ( "fmt" "sort" ) func main() { m := map[int]string{1:"a",2:"b",3:"c",4:"d",5:"e"} //定义map,map没有索引 s := make([]int,len(m))//定义slice,slice以索引为固定的key i :=0 for k,_ := range m{ s[i] = k //把map中全部的key存在slice中 i++ } sort.Ints(s)//使用sort进程排序,把map中的key排序 fmt.Println(s) } [1 2 3 4 5] package main import ( "fmt" "sort" ) func main() { m := map[int]string{1:"a",2:"b",3:"c",4:"d",5:"e"} //定义map,map没有索引 fmt.Println(len(m)) s := make([]int,len(m))//定义slice,slice以索引为固定的key i :=0 for k,_ := range m{ s[i] = k //把map中全部的key存在slice中 i++ } sort.Ints(s) fmt.Println(s) for j := range s{ fmt.Println(m[j+1]) } } 5 [1 2 3 4 5] a b c d e
package main import ( "fmt" ) func main() { m1 := map[int]string{1:"a",2:"b",3:"c",4:"d",5:"e"} fmt.Println(m1) m2 := make(map[string]int) for k,v := range m1{ m2[v] =k } fmt.Println(m2) } map[3:c 4:d 5:e 1:a 2:b] map[e:5 a:1 b:2 c:3 d:4]
程序正确运行后应输出以下结果:
Go 函数 不支持 嵌套、重载和默认参数
但支持如下特性:
无需声明原型、不定长度变参、多返回值、命名返回值参数
匿名函数、闭包
定义函数使用关键字 func,且左大括号不能另起一行
函数也能够做为一种类型使用
Go 语言函数定义格式以下:
func function_name( [parameter list] ) [return_types] { 函数体 }
函数定义解析:
func:函数由 func 开始声明
function_name:函数名称,函数名和参数列表一块儿构成了函数签名。
parameter list]:参数列表,参数就像一个占位符,当函数被调用时,你能够将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也能够不包含参数。
return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不须要返回值,这种状况下 return_types 不是必须的。
函数体:函数定义的代码集合。
定义参数列表是一个int输入,另外一个string输入,无返回值 fun A(a int,b string){ } 定义参数列表是一个int输入,另外一个string输入,只有一个int类型返回值 fun A(a int,b string)int{ } 定义参数列表是一个int输入,另外一个string输入,返回类型是一个int+一个string+一个int fun A(a int,b string)(int,string,int){ } 定义参数列表是3个int输入,3个int型输出 fun A(a int,b int, c int)(a int,b int,c int){ } 能够简写为: fun A(a,b, c int)(a,b,c int){ }
命令返回值参数和不命名返回值参数有什么区别呢?
若是你要返回多个返回值的话,并且使用(a,b,c int)简写形式的话,你就必须命名返回值,否则它就不知道了
func A() (a,b,c int){ a,b,c = 1,2,3 //这里不是:=,由于已经在内存中给a,b,c分配过内存地址了 return a,b,c //这里也能够直接写return,由于已经定义了返回值变量和类型(a,b,c int),代码可读性要求返回值return后加上变量a,b,c }
若是这样定义返回值的话 (int,int,int),就能够不用命名返回值(不定义返回值是a,b,c)
func A() (int,int,int){ a,b,c := 1,2,3 //这里必须是:=,由于是首次初始化变量 return a,b,c //这里不能够只写return }
若是A是一串int型的数字,n个,我要计算A长得最大值,怎么写呢?
使用go中的不定长变参
package main import "fmt" func main() { A(1,2,3,4,5,6,7) } func A(a ...int){ //...就是不定长变参,A就是一个slice,能够打印出来 fmt.Println(a) } 输出:[1 2 3 4 5 6 7] package main import "fmt" func main() { A("a",1,2,3,4,5,6,7) } func A(b string,a ...int){ //若是A使用了不定长变参"...",不能够在...后边定义变量b,能够在a以前定义变量b fmt.Println(b,a) } 输出:a [1 2 3 4 5 6 7]
slice的值拷贝和直接slice地址拷贝有什么区别呢?
package main import "fmt" func main() { a :=1 A(a) fmt.Println(a) } func A(a int){ //若是A使用了不定长变参"..."定义slice A a=2 //值拷贝不会修改面main函数中a的值,能够理解为:局部变量修改不会影响全局变量的值 fmt.Println(a) } 2 1 package main import "fmt" func main() { a,b :=1,2 A(a,b) fmt.Println(a,b) } func A(s ...int){ //若是A使用了不定长变参"..."定义slice s s[0]=3//a=3,值拷贝不会影响main函数的内部a的值 s[1]=4//b=4,值拷贝不会影响main函数的内部b的值 fmt.Println(s) } [3 4] 1 2 package main import "fmt" func main() { s1 := []int{1,2,3,4} A(s1) fmt.Println(s1) } func A(s []int){ //若是A使用了不定长变参"..."定义slice A s[0]=5//在A()函数的内部修改影响到了main函数中的s1的值,这里拿到了slice的地址,拷贝修改了slice内存地址中的值,实际上就是对slice自己进行操做 s[1]=6 s[2]=7 s[3]=8 fmt.Println(s) } [5 6 7 8] [5 6 7 8]
若是我想对这种int,string,也进行内存地址值得拷贝覆盖操做,怎么作?
采用指针地址值传递,先取出地址,再赋值
package main import "fmt" func main() { a :=1 A(&a)//调用A()函数,因为A()要求输出是指针类型(一个地址值0xxxx),全部输入&a符合输入要求&a=0xxxx fmt.Println(a)//这里也打印2,说明内存中的*a的值已经被修改 } func A(a *int){ //定义了指针类型的a,a可能的值是a=0xxxxx *a=2 //把内存地址为0xxxx的变量*a从新赋值为2 fmt.Println(*a)//打印*a的变量值, } 2 2
函数也是一种数据类型,给个例子
package main import "fmt" func main() { a :=A //这里a就是A的复制品 a() } func A(){ fmt.Println("Func A") } Func A
什么是匿名函数呢?
package main import "fmt" func main() { a := func() { //定义a是匿名函数,能够调用而且打印Func A fmt.Println("Func A") } a() } Func A
那么什么是闭包?
package main import "fmt" func main() { f := closure(10) //调用闭包函数closure(),返回一个匿名函数给f,赋值x=10 /* 此时f就是匿名函数: func(y int)int { fmt.Printf("%p\n",&x) return x + y } */ fmt.Println(f(1)) //第一次调用func(),x=10,y=1,return 11 fmt.Println(f(2)) //第二次调用func(),x=10,y=2,return 12 } func closure(x int) func(int) int { //闭包函数的做用是返回一个匿名函数 fmt.Printf("%p\n",&x) //第一次调用闭包函数打印x变量地址0xc420012070 return func(y int)int { fmt.Printf("%p\n",&x) //第2、三次调用闭包函数打印x变量地址0xc420012070,三次相同, return x + y } } 0xc420012070 0xc420012070 11 0xc420012070 12
实例
如下实例为 max() 函数的代码,该函数传入两个整型参数 num1 和 num2,并返回这两个参数的最大值:
//函数返回两个数的最大值 func max(num1, num2 int) int { // 声明局部变量 var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
函数调用
当建立函数时,你定义了函数须要作什么,经过调用改函数来执行指定任务。
调用函数,向函数传递参数,并返回值,例如:
package main import "fmt" func main() { /* 定义局部变量 */ var a int = 100 var b int = 200 var ret int /* 调用函数并返回最大值 */ ret = max(a, b) fmt.Printf( "最大值是 : %d\n", ret ) } /* 函数返回两个数的最大值 */ func max(num1, num2 int) int { /* 定义局部变量 */ var result int if (num1 > num2) { result = num1 } else { result = num2 } return result } 以上实例在 main() 函数中调用 max()函数,执行结果为: 最大值是 : 200
函数返回多个值
Go 函数能够返回多个值,例如:
package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("Mahesh", "Kumar") fmt.Println(a, b) } 以上实例执行结果为: Kumar Mahesh
传递类型 | 描述 |
---|---|
值传递 | 值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中若是对参数进行修改,将不会影响到实际参数。 |
引用传递 | 引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。 |
默认状况下,Go 语言使用的是值传递,即在调用过程当中不会影响到实际参数。
函数用法 函数用法 |
描述 |
---|---|
函数做为值 | 函数定义后可做为值来使用 |
闭包 | 闭包是匿名函数,可在动态编程中使用 |
方法 | 方法就是一个包含了接受者的函数 |
执行方式相似其它语言中的析构函数,在函数体执行结束后
按照调用顺序的相反顺序逐个执行(先进后出,后进先出)
即便函数发生严重错误也会执行
支持匿名函数的调用
经常使用于资源清理、文件关闭、解锁以及记录时间等操做
经过与匿名函数配合可在return以后修改函数计算结果
若是函数体内某个变量做为defer时匿名函数的参数,则在定义defer时即已经得到了拷贝,不然则是引用某个变量的地址
Go 没有异常机制,但有 panic/recover 模式来处理错误
Panic 能够在任何地方引起,但recover只有在defer调用的函数中有效
package main import "fmt" func main() { fmt.Println("a") defer fmt.Println("b") defer fmt.Println("c")//先调用打印c,在调用打印b } a c b package main import "fmt" func main() { for i :=0;i < 3 ;i ++{ defer fmt.Println(i) } } 2 //打印结果是2,1,0,而不是0,1,2 1 0 package main import "fmt" func main() { for i :=0;i < 3 ;i ++{//这里循环结束的时候i=3,闭包中的匿名函数调用的i=3,因此三次打印出来的值都是3 defer func(){ fmt.Println(i) //引用局部变量i,在退出for循环体的时候,i=3,在main函数return的时候,开始执行defer语句,i=3,因此所有打印3 }() //这个括号的意思是调用这个函数,能够理解为defer a() } } 3 3 3 package main import "fmt" func main() { A() B() C() } func A() { fmt.Println("Func A") } func B() { defer func(){//定义好defer遇到panic后Recover,必须在出现panic以前就定义好defer处理函数 if err := recover();err !=nil{ fmt.Println("Recover in B") } }() panic("Panic in B ")//定义panic } func C(){ fmt.Println("Func C") } Func A Recover in B Func C
package main import "fmt" func main() { var fs = [4]func(){}//定义fs是func类型的slice for i :=0;i<4;i++{ defer fmt.Println("defer i = ",i)//i做为一个参数,值拷贝传递,正常输出0,1,2,3,可是使用了defer定义,因此出书3,2,1,0 defer func(){ fmt.Println("defer_closure i =",i)//闭包匿名函数,外层循环结束,i=4,因此打印defer_closure i = 4 }() fs[i] = func() { fmt.Println("closure i =",i)//先将这些匿名函数存在func类型的slice中,这里i来自于外层的for循环,外层for循环结束以后引用地址内容值i=4,因此输出closure i = 4 } } for _,f := range fs{//调用fs,打印closure i = 4 f() } } closure i = 4 closure i = 4 closure i = 4 closure i = 4 defer_closure i = 4 defer i = 3 defer_closure i = 4 defer i = 2 defer_closure i = 4 defer i = 1 defer_closure i = 4 defer i = 0