1、1.11.21.31.3.11.3.21.3.31.41.4.11.4.21.4.31.4.41.4.51.4.61.4.71.4.7.11.4.7.21.4.7.31.4.7.41.4.7.51.51.5.11.5.21.5.31.5.41.5.51.5.61.5.6.1TableofContentsHOWdoGoWhyGogo环境的安装第一个Go语言程序go的命令行参数Vim配置Go语法高亮基础语法数据类型变量常量运算符条件语句循环语句函数及变量作用域函数闭包方法变量作用域main函数和init函数高级语法go中的指针go的数组go的结构体切片defer方法基于指针接收类型的方法11.5.
2、6.21.5.6.31.5.6.41.5.6.51.5.71.5.7.11.5.81.5.91.61.6.11.6.1.11.6.1.21.6.1.31.6.1.41.6.21.6.2.11.6.2.21.6.2.31.6.2.41.6.2.51.6.2.61.6.31.6.3.11.6.3.21.6.3.31.6.3.41.6.3.51.6.3.61.6.41.6.4.1方法值和方法表达式封装继承多态接口类型断言文件操作go与JSON并发与同步Goroutine案例:并发的clock时钟服务案例:并发的echo服务runtimego的goroutine是怎么实现的(来自知乎)Channel无
3、缓冲的channel串联的channel(pipeline)单方向的channel带缓存的channel基于select的多路复用案例:并发的网络聊天室服务Channel整理小结Channel类型Blocking、RangeselectTimer和TickerClose同步共享变量竞争条件21.71.7.11.7.21.7.31.81.8.11.8.21.8.31.91.9.11.9.21.9.31.9.41.9.51.101.10.11.10.1.11.10.1.21.10.1.3包和工具导包和声明构建包内部包实现简单web服务器Web工作方式go搭建一个简单的web服务器模板Beego框架
4、简介及设计思路快速入门路由设置参数设置ORMiHome爱家租房相关设计RESTfulAPI用户相关业务房屋相关业务订单相关业务3HowdoGOLang?本教材主要go语言入门级别,要求有一定的Linux基础和其他编程语言基础,本教程可以快速熟悉go语言,并对go完成实际应用编著:刘丹冰邮箱:danbing_HOWdoGo4WhyGo?现有的工程语言的一些缺点1phppythonruby系列这些都是动态语言,性能太慢不说,一旦代码规模庞大就很难控制代码质量。2javascalac#系列这一类语言性能强大内库完善,带着一个高效的gc(内存垃圾回收机制),看起来不错。但同样他们也存在一个巨大的缺点,
5、带一个硕大的运行时。导致各种资源消耗。同时java语法的繁琐也经常被人诟病。3c/c+系列这一类语言偏偏对处理字符串比较弱。而且没有gc。很难写出稳定性强的web程序。而且c+的编译性能弱爆了,你开一个大型项目,随便make一下,一天就这么过去了。那么go语言呢?业界一直需要一个轻量级,高性能,语法简洁同时带有一个完善的gc的编程语言,这个时候,刚好google的go语言发布,有强大的公司背景,go语言开发者也都是业界的大牛。而且每一个发布版质量可靠,性能稳定。自然不出意外收获了广泛的重视。go语言虽然很多特性都被人诟病,语法层面乏善可陈,可是他毕竟解决了码农的痛点。迅速形成了一个强大的社区。
6、强大的社区又会导致语言进一步强化自身,形成正反馈,想不成功都难。WhyGo5WhyGo6Go环境的安装Go语言支持以下系统:LinuxFreeBSDMacOSX(也称为Darwin)Window安装包下载地址为:https:/golang.org/dl/。安装go语言如果要升级之前的老版本需要先将之前的版本删除掉。Linux,MacOSX,andFreeBSDtarballs下载并且解压到解压go安装包到$HOME路径下例如:tar-C$HOME/-zxvfgo1.9.linux-amd64.tar.gzgo环境的安装7如果成功会在$HOME/路径下出现go/文件夹打开/.profile文件添
7、加如下代码exportGOROOT=$HOME/goexportPATH=$PATH:$GOROOT/binexportGOPATH=$HOME/workspace/goGOROOT为go语言源码的跟路径,PATH是需要配置go指令的查找路径,GOPATH是我们工作go语言的工作目录最后source/.profilego环境的安装8第一个Go语言程序packagemainimportfmtfuncmain()/*简单的程序万能的helloworld*/fmt.Println(HelloGo)终端运行$goruntest1_hello.goHelloGo$gorun表示直接编译go语言并执行应用
8、程序,一步完成你也可以先编译,然后再执行$gobuildtest1_hello.go$./test1_helloHelloGo第一行代码packagemain定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:packagemain。packagemain表示一个可独立执行的程序,每个Go应用程序都包含一个名为main的包。下一行importfmt告诉Go编译器这个程序需要使用fmt包(的函数,或其他元素),fmt包实现了格式化IO(输入/输出)的函数。下一行funcmain()是程序开始执行的函数。main函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的
9、函数(如果有init()函数则会先执行该函数)。第一个Go语言程序9注意:这里面go语言的语法,定义函数的时候,必须和函数名在同一行,不能另起一行。下一行/*.*/是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以/开头的单行注释。多行注释也叫块注释,均已以/*开头,并以*/结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。下一行fmt.Println(.)可以将字符串输出到控制台,并在最后自动增加换行字符n。使用fmt.Print(hello,worldn)可以得到相同的结果。Print和Println这两个函数也支持使用变量,如:fmt
10、.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量arr输出到控制台。第一个Go语言程序10go的命令行参数packagemainimportfmtimportosfuncmain()vars,sepstringfori:=1;ilen(os.Args);i+s+=sep+os.Argsisep=fmt.Println(s)执行goruntest.gohahaheihei结果:hahaheiheigo的命令行参数11I.配置Vim-go语法高亮vim配置,Vim-go是当前使用最为广泛的用于搭建Golang开发环境的vim插件,于是我们采用vim-go插件。vim-go
11、利用开源Vim插件管理器安装,gmarik/Vundle.vim是目前被推荐次数更多的Vim插件管理器。可以参照官方介绍https:/ =将一个变量的值赋值给另一个变量时,如: j=i,实际上是在内存中将i的值进行了拷贝:你可以通过&i来获取变量i的内存地址,例如:0 xf840000040(每次的地址都可能不一样)。值类型的变量的值存储在栈中。内存地址会根据机器的不同而有所不同,甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台机器可能有不同的存储器布局,并且位置分配也可能不同。更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存。一个引用类型的变量r1存储的是r1的
12、值所在的内存地址(数字),或内存地址中第一个字所在的位置。这个内存地址为称之为指针,这个指针实际上也被存在另外的某一个字中。变量25同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。当使用赋值语句r2=r1时,只有引用(地址)被复制。如果r1的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2也会受到影响。变量26常量常量是一个简单值的标识符,在程序运行时,不会被修改的量。常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符
13、串型。常量的定义格式:constidentifiertype=value你可以省略类型说明符type,因为编译器可以根据变量的值来推断其类型。显式类型定义:constbstring=abc隐式类型定义:constb=abc例如:常量27packagemainimportfmtfuncmain()constLENGTHint=10constWIDTHint=5varareaintconsta,b,c=1,false,str/多重赋值area=LENGTH*WIDTHfmt.Printf(面积为:%dn,area)println(a,b,c)以上实例运行结果为:面积为:501falsestr常量还
14、可以用作枚举:const(Unknown=0Female=1Male=2)数字0、1和2分别代表未知性别、女性和男性。常量可以用len(),cap(),unsafe.Sizeof()常量计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过:常量28packagemainimportunsafeconst(a=abcb=len(a)c=unsafe.Sizeof(a)funcmain()println(a,b,c)输出结果为:abc,3,16unsafe.Sizeof(a)输出的结果是16。字符串类型在go里是个结构,包含指向底层数组的指针和长度,这两部分每部分都是8个字节,所以字符串
15、类型大小为16个字节。优雅的常量iota有些概念有名字,并且有时候我们关注这些名字,甚至(特别)是在我们代码中。const(CCVisa=VisaCCMasterCard=MasterCardCCAmericanExpress=AmericanExpress)在其他时候,我们仅仅关注能把一个东西与其他的做区分。有些时候,有些时候一件事没有本质上的意义。比如,我们在一个数据库表中存储产品,我们可能不想以string存储他们的分类。我们不关注这个分类是怎样命名的,此外,该名字在市场上一直在变化。我们仅仅关注它们是怎么彼此区分的。常量29const(CategoryBooks=0CategoryHe
16、alth=1CategoryClothing=2)使用0,1,和2代替,我们也可以选择17,43,和61。这些值是任意的。在Go,常量有许多微妙之处。当用好了,可以使得代码非常优雅且易维护的。自增长在golang中,一个方便的习惯就是使用 iota标示符,它简化了常量用于增长数字的定义,给以上相同的值以准确的分类。const(CategoryBooks=iota/0CategoryHealth/1CategoryClothing/2)自定义类型自增长常量经常包含一个自定义类型,允许你依靠编译器。typeStereotypeint/这是一个自定义类型Stereotype实际上就是int类型con
17、st(TypicalNoobStereotype=iota/0TypicalHipster/1TypicalUnixWizard/2TypicalStartupFounder/3)常量30如果一个函数以 int作为它的参数而不是 Stereotype,如果你给它传递一个 Stereotype,它将在编译器期出现问题。如下packagemainimportfmttypeStereotypeintconst(TypicalNoobStereotype=iota/0TypicalHipster/1TypicalUnixWizard/2TypicalStartupFounder/3)/*-*/*bri
18、ef测试const形参匹配类型*paramint*returns*string类型*/*-*/funcCountAllTheThings(iint)string/返回一个字符串Sprintf是创建一个字符串returnfmt.Sprintf(thereare%dthings,i)funcmain()n:=TypicalHipster/虽然自定义类型,但是传参也是会报错.fmt.Println(CountAllTheThings(n)常量31output:$cannotuseTypicalHipster(typeStereotype)astypeintinargumenttoCountAllTh
19、eThings相反亦是成立的。给一个函数以 Stereotype作为参数,你不能给它传递 int。funcSoSayethThe(characterStereotype)stringvarsstringswitchcharactercaseTypicalNoob:s=Imaconfusedninjarockstar.caseTypicalHipster:s=EverythingwasbetterweprogrammeduphillandbarefootinthesnowontheSUTX5918caseTypicalUnixWizard:s=sudogrepawksed%!#?!1!caseT
20、ypicalStartupFounder:s=exploitcompellingconvergencetosyndicategeo-targetedsolutionsreturnsfuncmain()i:=2fmt.Println(SoSayethThe(i)/output:/cannotusei(typeint)astypeStereotypeinargumenttoSoSayethThe但是戏剧性的是,尽管如此,你可以传递一个数值常量,然后它却能够工作。常量32funcmain()fmt.Println(SoSayethThe(0)/output:/Imaconfusedninjarock
21、star.这是因为常量在Go中是弱类型直到它使用在一个严格的上下文环境中。另外,我们可以使用下划线跳过不想要的值。typeAudioOutputintconst(OutMuteAudioOutput=iota/0OutMono/1OutStereo/2_OutSurround/5)iota和表达式 iota可以做更多事情,而不仅仅是increment。更精确地说, iota总是用于increment,但是它可以用于表达式,在常量中的存储结果值。常量33typeAllergenintconst(IgEggsAllergen=1iota/10whichis00000001IgChocolate/1
22、1whichis00000010IgNuts/12whichis00000100IgStrawberries/13whichis00001000IgShellfish/14whichis00010000)这个工作是因为当你在一个 const组中仅仅有一个标示符在一行的时候,它将使用增长的 iota取得前面的表达式并且再运用它,。在Go语言的spec中,这就是所谓的隐性重复最后一个非空的表达式列表.如果你对鸡蛋,巧克力和海鲜过敏,把这些bits翻转到“on”的位置(从左到右映射bits)。然后你将得到一个bit值 00010011,它对应十进制的19。fmt.Println(IgEggs|IgC
23、hocolate|IgShellfish)/output:/19常量34typeByteSizefloat64const(_=iota/ignorefirstvaluebyassigningtoblankidentifierKBByteSize=1(10*iota)/1(10*1)MB/1(10*2)GB/1(10*3)TB/1(10*4)PB/1(10*5)EB/1(10*6)ZB/1(10*7)YB/1检查左边值是否大于右边值,如果是返回True否则返回False。(AB)为False检查左边值是否小于右边值,如果是返回True否则返回False。(A=检查左边值是否大于等于右边值,如果是
24、返回True否则返回False。(A=B)为False=检查左边值是否小于等于右边值,如果是返回True否则返回False。(A=B)为True以下实例演示了关系运算符的用法:运算符39packagemainimportfmtfuncmain()varaint=21varbint=10if(a=b)fmt.Printf(第一行-a等于bn)elsefmt.Printf(第一行-a不等于bn)if(ab)fmt.Printf(第三行-a大于bn)elsefmt.Printf(第三行-a不大于bn)/*Letschangevalueofaandb*/a=5b=20if(a=a)fmt.Printf
25、(第五行-b大于等于an)以上实例运行结果:运算符40第一行-a不等于b第二行-a不小于b第三行-a大于b第四行-a小于等于b第五行-b大于等于a逻辑运算符下表列出了所有Go语言的逻辑运算符。假定A值为True,B值为False。运算符描述实例&逻辑AND运算符。如果两边的操作数都是True,则条件True,否则为False。(A&B)为False逻辑OR运算符。如果两边的操作数有一个True,则条件True,否则为False。(AB)为True!逻辑NOT运算符。如果条件为True,则逻辑NOT条件False,否则为True。!(A&B)为True以下实例演示了逻辑运算符的用法:运算符41p
26、ackagemainimportfmtfuncmain()varabool=truevarbbool=falseif(a&b)fmt.Printf(第一行-条件为truen)if(a|b)fmt.Printf(第二行-条件为truen)/*修改a和b的值*/a=falseb=trueif(a&b)fmt.Printf(第三行-条件为truen)elsefmt.Printf(第三行-条件为falsen)if(!(a&b)fmt.Printf(第四行-条件为truen)以上实例运行结果:第二行-条件为true第三行-条件为false第四行-条件为true位运算符位运算符对整数在内存中的二进制位进行
27、操作。运算符42下表列出了位运算符&,|,和的计算:A=00111100B=00001101-A&B=00001100A|B=00111101AB=00110001A=11000011Go语言支持的位运算符如下表所示。假定A为60,B为13:运算符43运算符描述实例&按位与运算符&是双目运算符。其功能是参与运算的两数各对应的二进位相与。(A&B)结果为12,二进制为00001100按位或运算符是双目运算符。其功能是参与运算的两数各对应的二进位相或(AB)结果为61,二进制为00111101按位异或运算符是双目运算符。其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。
28、(AB)结果为49,二进制为00110001左移运算符是双目运算符。左移n位就是乘以2的n次方。其功能把左边的运算数的各二进位全部左移若干位,由右边的数指定移动的位数,高位丢弃,低位补0。A右移运算符是双目运算符。右移n位就是除以2的n次方。其功能是把左边的运算数的各二进位全部右移若干位,右边的数指定移动的位数。A2结果为15,二进制为00001111运算符44以下实例演示了逻辑运算符的用法:packagemainimportfmtfuncmain()varauint=60/*60=00111100*/varbuint=13/*13=00001101*/varcuint=0c=a&b/*12=
29、00001100*/fmt.Printf(第一行-c的值为%dn,c)c=a|b/*61=00111101*/fmt.Printf(第二行-c的值为%dn,c)c=ab/*49=00110001*/fmt.Printf(第三行-c的值为%dn,c)c=a2/*15=00001111*/fmt.Printf(第五行-c的值为%dn,c)以上实例运行结果:第一行-c的值为12第二行-c的值为61第三行-c的值为49第四行-c的值为240第五行-c的值为15运算符45赋值运算符下表列出了所有Go语言的赋值运算符。运算符描述实例=简单的赋值运算符,将一个表达式的值赋给一个左值C=A+B将A+B表达式结
30、果赋值给C+=相加后再赋值C+=A等于C=C+A-=相减后再赋值C-=A等于C=C-A*=相乘后再赋值C*=A等于C=C*A/=相除后再赋值C/=A等于C=C/A%=求余后再赋值C%=A等于C=C%A=左移后赋值C=2等于C=C=右移后赋值C=2等于C=C2&=按位与后赋值C&=2等于C=C&2=按位异或后赋值C=2等于C=C2=按位或后赋值C=2等于C=C2以下实例演示了赋值运算符的用法:packagemainimportfmtfuncmain()运算符46varaint=21varcintc=afmt.Printf(第1行-=运算符实例,c值为=%dn,c)c+=afmt.Printf(第
31、2行-+=运算符实例,c值为=%dn,c)c-=afmt.Printf(第3行-=运算符实例,c值为=%dn,c)c*=afmt.Printf(第4行-*=运算符实例,c值为=%dn,c)c/=afmt.Printf(第5行-/=运算符实例,c值为=%dn,c)c=200;c=2fmt.Printf(第6行-=2fmt.Printf(第7行-=运算符实例,c值为=%dn,c)c&=2fmt.Printf(第8行-&=运算符实例,c值为=%dn,c)c=2fmt.Printf(第9行-=运算符实例,c值为=%dn,c)c|=2fmt.Printf(第10行-|=运算符实例,c值为=%dn,c)以
32、上实例运行结果:运算符47第1行-=运算符实例,c值为=21第2行-+=运算符实例,c值为=42第3行-=运算符实例,c值为=21第4行-*=运算符实例,c值为=441第5行-/=运算符实例,c值为=21第6行-=运算符实例,c值为=200第8行-&=运算符实例,c值为=0第9行-=运算符实例,c值为=2第10行-|=运算符实例,c值为=2其他运算符下表列出了Go语言的其他运算符。运算符描述实例&返回变量存储地址&a;将给出变量的实际地址。*指针变量。*a;是一个指针变量以下实例演示了其他运算符的用法:运算符48packagemainimportfmtfuncmain()varaint=4va
33、rbint32varcfloat32varptr*int/*运算符实例*/fmt.Printf(第1行-a变量类型为=%Tn,a);fmt.Printf(第2行-b变量类型为=%Tn,b);fmt.Printf(第3行-c变量类型为=%Tn,c);/*&和*运算符实例*/ptr=&a/*ptr包含了a变量的地址*/fmt.Printf(a的值为%dn,a);fmt.Printf(*ptr为%dn,*ptr);以上实例运行结果:第1行-a变量类型为=int第2行-b变量类型为=int32第3行-c变量类型为=float32a的值为4*ptr为4运算符优先级有些运算符拥有较高的优先级,二元运算符的
34、运算方向均是从左至右。下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低:运算符49优先级运算符7!6*/%&5+-4=!=3-2&1当然,你可以通过使用括号来临时提升某个表达式的整体运算优先级。以上实例运行结果:packagemainimportfmtfuncmain()varaint=20varbint=10varcint=15vardint=5vareint;e=(a+b)*c/d;/(30*15)/5fmt.Printf(a+b)*c/d的值为:%dn,e);e=(a+b)*c)/d;/(30*15)/5fmt.Printf(a+b)*c)/d的值为:%dn,e);e=(
35、a+b)*(c/d);/(30)*(15/5)fmt.Printf(a+b)*(c/d)的值为:%dn,e);e=a+(b*c)/d;/20+(150/5)fmt.Printf(a+(b*c)/d的值为:%dn,e);运算符50以上实例运行结果:(a+b)*c/d的值为:90(a+b)*c)/d的值为:90(a+b)*(c/d)的值为:90a+(b*c)/d的值为:50运算符51条件语句条件语句需要开发者通过指定一个或多个条件,并通过测试条件是否为true来决定是否执行指定语句,并在条件为false的情况在执行另外的语句。下图展示了程序语言中条件语句的结构:if语句条件语句52packagem
36、ainimportfmtfuncmain()/*定义局部变量*/varaint=10/*使用if语句判断布尔表达式*/ifa20/*如果条件为true则执行以下语句*/fmt.Printf(a小于20n)fmt.Printf(a的值为:%dn,a)以上代码执行结果为:a小于20a的值为:10if.else语句if语句后可以使用可选的else语句,else语句中的表达式在布尔表达式为false时执行。条件语句53packagemainimportfmtfuncmain()/*局部变量定义*/varaint=100;/*判断布尔表达式*/ifa20/*如果条件为true则执行以下语句*/fmt.P
37、rintf(a小于20n);else/*如果条件为false则执行以下语句*/fmt.Printf(a不小于20n);fmt.Printf(a的值为:%dn,a);以上代码执行结果为:a不小于20a的值为:100switch语句switch语句用于基于不同条件执行不同动作,每一个case分支都是唯一的,从上直下逐一测试,直到匹配为止。switch语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加break条件语句54packagemainimportfmtfuncmain()/*定义局部变量*/vargradestring=Bvarmarksint=90switchmarkscas
38、e90:grade=Acase80:grade=Bcase50,60,70:grade=Cdefault:grade=Dswitchcasegrade=A:fmt.Printf(优秀!n)casegrade=B,grade=C:fmt.Printf(良好n)casegrade=D:fmt.Printf(及格n)casegrade=F:fmt.Printf(不及格n)default:fmt.Printf(差n);fmt.Printf(你的等级是%sn,grade);以上代码执行结果为:优秀!你的等级是Aswitch语句还可以被用于type-switch来判断某个interface变量中实际存储的
39、变量类型。条件语句55TypeSwitch语法格式如下:switchx.(type)casetype:statement(s);casetype:statement(s);/*你可以定义任意个数的case*/default:/*可选*/statement(s);例如:packagemainimportfmtfuncmain()varxinterfaceswitchi:=x.(type)casenil:fmt.Printf(x的类型:%T,i)caseint:fmt.Printf(x是int型)casefloat64:fmt.Printf(x是float64型)casefunc(int)floa
40、t64:fmt.Printf(x是func(int)型)casebool,string:fmt.Printf(x是bool或string型)default:fmt.Printf(未知型)以上代码执行结果为:条件语句56x的类型:条件语句57语言for循环for循环是一个循环控制结构,可以执行指定次数的循环。语法Go语言的For循环有3中形式,只有其中的一种使用分号。和C语言的for一样:forinit;condition;post和C的while一样:forcondition和C的for(;)一样:forinit:一般为赋值表达式,给控制变量赋初值;condition:关系表达式或逻辑表达式,
41、循环控制条件;post:一般为赋值表达式,给控制变量增量或减量。for循环的range格式可以对slice、map、数组、字符串等进行迭代循环。格式如下:forkey,value:=rangeoldMapnewMapkey=value循环语句58packagemainimportfmtfuncmain()varbint=15varaintnumbers:=6int1,2,3,5/*for循环*/fora:=0;a10;a+fmt.Printf(a的值为:%dn,a)foraba+fmt.Printf(a的值为:%dn,a)fori,x:=rangenumbersfmt.Printf(第%d位x
42、的值=%dn,i,x)以上实例运行输出结果为:循环语句59a的值为:0a的值为:1a的值为:2a的值为:3a的值为:4a的值为:5a的值为:6a的值为:7a的值为:8a的值为:9a的值为:1a的值为:2a的值为:3a的值为:4a的值为:5a的值为:6a的值为:7a的值为:8a的值为:9a的值为:10a的值为:11a的值为:12a的值为:13a的值为:14a的值为:15第0位x的值=1第1位x的值=2第2位x的值=3第3位x的值=5第4位x的值=0第5位x的值=0循环控制语句循环控制语句可以控制循环体内语句的执行过程。GO语言支持以下几种循环控制语句:控制语句描述循环语句60break语句经常用
43、于中断当前for循环或跳出switch语句continue语句跳过当前循环的剩余语句,然后继续进行下一轮循环。goto语句将控制转移到被标记的语句。goto语句packagemainimportfmtfuncmain()/*定义局部变量*/varaint=10/*循环*/LOOP:foranum2)result=num1elseresult=num2returnresult函数调用当创建函数时,你定义了函数需要做什么,通过调用改函数来执行指定任务。调用函数,向函数传递参数,并返回值,例如:函数65packagemainimportfmtfuncmain()/*定义局部变量*/varaint=1
44、00varbint=200varretint/*调用函数并返回最大值*/ret=max(a,b)fmt.Printf(最大值是:%dn,ret)/*函数返回两个数的最大值*/funcmax(num1,num2int)int/*定义局部变量*/varresultintif(num1num2)result=num1elseresult=num2returnresult以上实例在main()函数中调用max()函数,执行结果为:最大值是:200函数返回多个值Go函数可以返回多个值,例如:函数66packagemainimportfmtfuncswap(x,ystring)(string,string
45、)returny,xfuncmain()a,b:=swap(Mahesh,Kumar)fmt.Println(a,b)以上实例执行结果为:KumarMahesh函数参数函数如果使用参数,该变量可称为函数的形参。形参就像定义在函数体内的局部变量。调用函数,可以通过两种方式来传递参数:值传递:值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。默认情况下,Go语言使用的是值传递,即在调用过程中不会影响到实际参数。以下定义了swap()函数:函数67/*定义相互交换值的函数*/funcswap(x,yint)intvartempinttemp=
46、x/*保存x的值*/x=y/*将y值赋给x*/y=temp/*将temp值赋给y*/returntemp;接下来,让我们使用值传递来调用swap()函数:函数68packagemainimportfmtfuncmain()/*定义局部变量*/varaint=100varbint=200fmt.Printf(交换前a的值为:%dn,a)fmt.Printf(交换前b的值为:%dn,b)/*通过调用函数来交换值*/swap(a,b)fmt.Printf(交换后a的值:%dn,a)fmt.Printf(交换后b的值:%dn,b)/*定义相互交换值的函数*/funcswap(x,yint)intvar
47、tempinttemp=x/*保存x的值*/x=y/*将y值赋给x*/y=temp/*将temp值赋给y*/returntemp;以下代码执行结果为:交换前a的值为:100交换前b的值为:200交换后a的值:100交换后b的值:200函数69引用传递引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。引用传递指针参数传递到函数内,以下是交换函数swap()使用了引用传递:/*定义交换值函数*/funcswap(x*int,y*int)vartempinttemp=*x/*保持x地址上的值*/*x=*y/*将y值赋给x*/*y=temp/*将t
48、emp值赋给y*/以下我们通过使用引用传递来调用swap()函数:函数70packagemainimportfmtfuncmain()/*定义局部变量*/varaint=100varbint=200fmt.Printf(交换前,a的值:%dn,a)fmt.Printf(交换前,b的值:%dn,b)/*调用swap()函数*&a指向a指针,a变量的地址*&b指向b指针,b变量的地址*/swap(&a,&b)fmt.Printf(交换后,a的值:%dn,a)fmt.Printf(交换后,b的值:%dn,b)funcswap(x*int,y*int)vartempinttemp=*x/*保存x地址上
49、的值*/*x=*y/*将y值赋给x*/*y=temp/*将temp值赋给y*/以上代码执行结果为:交换前,a的值:100交换前,b的值:200交换后,a的值:200交换后,b的值:100函数71函数作为值Go语言可以很灵活的创建函数,并作为值使用。以下实例中我们在定义的函数中初始化一个变量,该函数仅仅是为了使用内置函数math.sqrt(),实例为:packagemainimport(fmtmath)funcmain()/*声明函数变量*/getSquareRoot:=func(xfloat64)float64returnmath.Sqrt(x)/*使用函数*/fmt.Println(getS
50、quareRoot(9)以上代码执行结果为:3函数72闭包Go语言支持匿名函数,可作为闭包。匿名函数是一个内联语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。以下实例中,我们创建了函数getSequence(),返回另外一个函数。该函数的目的是在闭包中递增i变量,代码如下:packagemainimportfmtfuncgetSequence()func()inti:=0returnfunc()inti+=1returnifuncmain()/*nextNumber为一个函数,函数i为0*/nextNumber:=getSequence()/*调用nextNumber函数