go语言学习笔记(初级)最近一直在学习go语言,因此打算学习的时候能够记录一下笔记。我这个人之前是从来没有记录笔记的习惯,一直以来都是靠强大的记忆力去把一些要点记住。读书的时候因为一直都是有一个很安静和很专心的环境,因此很多事情都能记得很清楚,思考的很透彻。但是随着年纪不断增加,也算是经历了很多的事情,加上工作有时会让人特别烦闷,很难把心好好静下来去学习,去思考大自然的终极奥秘,因此需要记录一些东西,这些东西一方面可以作为一种自我激励的机制,另一方面,也算是自己成长的轨迹吧。一.顺序编程1.变量go语言变量定义的关键字是var。类型放在变量名后:varv1intvarv2stringvarv3[10]int//数组varv4[]int//切片varv5struct{//结构体fint}varv6*int//指针varv7map[string]int//mapvarv8func(aint)int//函数每一行不需要以分号作为结尾。var关键字还有一种是把多个变量的申明放在一起,var(v1stringv2int)2.变量初始化有人说,变量初始化有什么好提的,那么简单。是的,但是这里面确实还是有一些值得注意的点。varaint=10//完整定义vara=10//自动推断是int型a:=10//自动推断是int型,申明并未该变量赋值第三种初始化方式无疑是最简单的。但是要注意的是,这里面第三种方式是和特别的,比如varainta:=10等价于varaintvarainta=10这时候就会报一个重复定义的错误。3.变量赋值变量赋值和我们通常的语言基本是一致的,但是多了多重赋值功能。i,j=j,i这就直接实现了两个变量的交换。4.匿名变量go语言的函数是多返回值的,因此可能有些值并没有被用到,这时我们就需要一个占位符去忽略这些返回值。funcGetName()(firstName,lastName,nickNamestring){returnMay,Chan,ChibiMaruko}_,_,nickName:=GetName()5.定义常量通过const关键字,可以用来定义常量。constPifloat64=3.1415926constzero=0.0//自动推断类型const(//多定义sizeint64=10hello=-1)constu,vfloat32=0.0,3.0//多重赋值consta,b,c=1,2,“hello”//自动推断类型常量的定义也可以跟一个表达式,但是这个表达式应该是编译的时候就可以求值的.constmask=13//正常constHome=os.GetEnv(HOME)//错误,运行时才能确定6.预定义常量这里要讲一个很有意思的东西,叫做iota.这个东西每一个const出现的位置被置为0,没出现一个iota出现,都自增1,到写一个const出现的时候,又置为0.const(c1=iota//0c2=iota//1c3=iota//2)constx=iota//x==0(因为iota又被重设为0了)consty=iota//y==0(同上)如果两个const赋值表达式是一样的,可以省略后面的赋值表达式.const(c1=iota//0c2//1c3//3)const(a=1iota//a==1(iota在每个const开头被重设为0)b//b==2c//c==4)6.枚举const(Sunday=iotaMondayTuesdayWednesdayThursdayFridaySaturdaynumberOfDays//这个常量没有导出)大写字母开头的包外可见,小写字母开头的包外不可见.7.类型整型int8,uint8,int16,uint16,int32,uint32,int64,uint64,int,uint,uintptr不同类型不能相互比较.浮点类型float32,float64涉及的一些运算比较简单,我们不做细讲.字符串类型下面我们展示一个完整的go语言程序,也是以helloworld为主题,毕竟helloworld是一个万斤油的主题.packagemainimportfmt//引入依赖包funcmain(){fmt.Println(hello,world!)}这基本上是一个最简单的程序了,但是对于我们的学习非常有用,用这个模板可以写出非常好的东西出来.字符串串本身非常简单,主要就是一些字符串操作,比如取特定位置的字符等.packagemainimportfmt//引入依赖包funcmain(){varstrstring=hello,world!fmt.Println(str)ch:=str[0]//取某个特定位置的字符fmt.Printf(%c\n,ch)length:=len(str)fmt.Println(length)//len用来获取长度str=你好,世界ch=str[0]fmt.Printf(%c\n,ch)length=len(str)fmt.Println(length)}输出结果为:hello,world!h12?13这正好说明[]和len都不能处理中文.字符串连接也是用+.字符串的遍历:packagemainimportfmt//引入依赖包funcmain(){varstrstring=hello,world!n:=len(str)fori:=0;in;i++{ch:=str[i]fmt.Printf(%c\n,ch)}}输出结果:hello,world!对于中文,结果是乱码,因为是一个字节一个字节输出的,但是默认是UTF8编码,一个中文对应3个字节.这里我们要提到一个range的关键字,它可以把字符串按键值对的方式返回.packagemainimportfmt//引入依赖包funcmain(){varstrstring=hello,world!你好,世界!for_,ch:=rangestr{fmt.Printf(%c\n,ch)}}输出结果为:hello,world!你好,世界!事实上,字符类型有两种,一种就是byte(uint8),另一种是rune.第一种遍历字符串ch是byte,而第二种是rune.数组数组这种类型是非常常见的[32]byte[2*N]struct{x,yint32}[1000]*float64[3][5]int[2][2][2]float64数组的遍历和字符串一样,这里不再重复.数组是值类型,在赋值时会拷贝一份.数组切片数组切片的概念比较复杂,它有点类似于c++中vector的概念,但又不完全一样.我们这里详细提几点.1.切片的创建切片有两种创建方式,一种是基于数组创建,另一种是用make创建.packagemainimportfmt//引入依赖包funcmain(){//从数组创建varmyArray[10]int=[10]int{1,2,3,4,5,6,7,8,9,10}varsa[]int=myArray[5:]for_,e:=rangesa{fmt.Println(e)}fmt.Println(len(sa))fmt.Println(cap(sa))//从make创建varmySlice2[]int=make([]int,5,10)for_,e:=rangemySlice2{fmt.Println(e)}fmt.Println(len(mySlice2))fmt.Println(cap(mySlice2))//赋值varmySlice3[]int=[]int{1,2,3}for_,e:=rangemySlice2{fmt.Println(e)}}slice是引用类型.packagemainimportfmt//引入依赖包functest(a[10]int){a[0]=10}funcprintArray(a[10]int){for_,e:=rangea{fmt.Println(e)}}funcmain(){varmyArray[10]int=[10]int{1,2,3,4,5,6,7,8,9,10}printArray(myArray)test(myArray)printArray(myArray)}输出结果:1234567891012345678910我们发现数组确实是按照值来传递.那么如果是slice呢,会发生什么?packagemainimportfmt//引入依赖包functest(a[]int){a[0]=10}funcprintArray(a[]int){for_,e:=rangea{fmt.Println(e)}}funcmain(){varmyArray[]int=[]int{1,2,3,4,5,6,7,8,9,10}printArray(myArray)test(myArray)printArray(myArray)}输出结果:12345678910102345678910确实是按照引用来传递的.append函数可以往切片尾部增加元素.mySlice=append(mySlice,1,2,3)mySlice=append(mySlice,mySlice2...)...表示把一个slice拆成元素来处理.packagemainimportfmt//引入依赖包funcmain(){varslice1[]int=make([]int,5,10)varslice2[]int=[]int{1,2,3}fmt.Println(slice1)fmt.Printf(%p\n,slice1)slice1=append(slice1,slice2...)fmt.Println(slice1)fmt.Printf(%p\n,slice1)slice1=append(slice1,slice2...)fmt.Println(slice1)fmt.Printf(%p\n,slice1)}输出结果:[00000]0xc820012190[00000123]0xc820012190[00000123123]0xc82005e000在这里我们看到,slice的地址是所随着内内存的改变而变化的,因此是需要仔细思考的.我个人不觉得go语言这种特性有什么好的,反正也是奇葩极了.不过slice还提供copy,也算是一些弥补吧.mapgo语言中,map使用非常简单.基本上看代码就会了.packagemainimportfmt//引入依赖包//定义一个Person的结构体typePersonstruct{namestringageint}funcmain(){vardicmap[string]Person=make(map[string]Person,100)//初始化mapdic[1234]=Person{name:lilei,age:100}dic[12345]=Person{name:hanmeimei,age:20}dic[123456]=Person{name:dagong,age:30}fmt.Println(dic)//删除dagongdelete(dic,123456)fmt.Println(dic)//查找某个keyvalue,ok:=dic[123456]ifok{fmt.Println(value)}value,ok=dic[1234]ifok{fmt.Println(value)}fork,v:=rangedic{fmt.Println(k+:+v.name)}}输出结果为:map[12345:{hanmeimei20}123456:{dagong30}1234:{lilei100}]map[1234:{lilei100}12345:{hanmeimei20}]{lilei100}12345:hanmeimei1234:lileimap很简单吧.数据结构我们讲完了,接下来可以看看代码的程序控制了.8.程序控制程序控制本人只提一些关键性的东西,不会啰嗦太多.switch语句switch语句不需要在每个case地下写