Go程序设计语言(一)本文译自RobPike的Go语言PPT教程–TheGoProgrammingLanguagePart1(updatedJune2011)。由于该教程的最新更新时间早于Go1版本发布,因此该PPT中的一些内容与Go1语言规范略有差异,到时我会在相应的地方做上注解。谁发明了GoGo语言的设计和实现工作是由Google的一个研发小组以及来自世界各地的大量贡献者共同完成的。联系方式:官网golang-nuts@golang.org:用户讨论组golang-dev@golang.org:开发者讨论组课程大纲第一部分:基础第二部分:类型(type)、方法(method)以及接口(interface)第三部分:并发(concurrency)与通信(communication)这个课程是关于Go程序设计语言的,而不是关于编程语言设计方法的,后者是一个单独的话题,不在此教程范围内。第一部分大纲动机基础-简单、熟悉的内容包与程序构建动机为什么要发明一门新语言?在当今世界,编程语言在某些方面不够给力:计算机运行速度快,但软件的构建慢。为了速度和安全性需进行必要的依赖分析。在类型上遇到了太多的阻碍。对垃圾收集以及并发的支持太差。多核被视为危机而不是机会。积极应对我们的目标是让编程重新回归快乐。兼有动态语言的感觉以及静态类型系统的安全性;编译成机器语言以获得更快的运行速度;真正在运行时支持GC(垃圾收集)以及并发;轻量级、灵活的类型系统;拥有方法(method),但却不是传统的OO(面向对象)语言。资源关于Go语言的更多背景资料请参见文档:文档包括:语言规范教程EffectiveGo标准库文档安装和How-to文档FAQs一个语言联系游乐场(在浏览器中运行Go程序)更多现状:编译器gc(KenThompson),又称6g,8g,5g继承自Plan9项目的编译器模型生成代码速度非常快不支持gcc直接链接gccgo(IanTaylor)更为熟悉的体系架构生成代码的速度没有gc那样快支持gcc直接链接支持32-bit和64-bitx86(amd64,x86-64)以及ARM。垃圾收集器,并发等都已实现。优秀且正逐步完善的标准库。基础是时候上一些代码了packagemainimportfmtfuncmain(){fmt.Print(Hello,世界\n)}语言基础假设熟悉其他C语言类的(C-like)编程语言,这里将快速浏览一些基础知识。这里大部分内容是简单的且熟悉的,也可能因此而有些沉闷,这里先说声道歉。接下来的两部分教程会包含很有趣的内容,不过我们首先需要打下良好基础。词法结构-传统中蕴含新意。-源码采用UTF-8编码。空格包括:空白,tab,换行,回车。-标识符由字母和数字组成(外加'_'),字母和数字都是Unicode编码。-注释:/*Thisisacomment;nonesting*///Soisthis.字面值(literals)类似C语言中的字面值,但数值不需要符号以及大小标志(后续会有更多这方面内容):230x0FF1.234e7类似C中的字符串,但字符串是Unicode/UTF-8编码的。同时,\xNN总是有2个数字;\012总是3;两个都是字节:Hello,world\n\xFF//1byte\u00FF//1Unicodechar,2bytesofUTF-8原生字符串:`\n\.abc\t\`==\\n\\.abc\\t\\语法概述基本上就是类C的语法,但使用反转的类型和声明,并使用关键字作为每个声明的开头。varaintvarb,c*int//注意与C的不同vard[]inttypeSstruct{a,bint}基本的控制结构也十分熟悉:ifa==b{returntrue}else{returnfalse}fori=0;i10;i++{…}注意:没有圆括号,但需要大括号。后续会有更多有关这方面的内容。分号分号作为语句终止符号,但:-如果前一个符号是语句的结尾,那词法分析程序将自动在行尾插入一个分号-注意:比JavaScript的规则更清晰和简单因此,下面的程序不需要分号:packagemainconstthree=3variint=threefuncmain(){fmt.Printf(%d\n,i)}在实际中,Go源码在for和if子句之外几乎都没有用到分号。数值类型数值类型(numerictypes)是原生内置的,也是为大家所熟知的:intuintint8uint8=byteint16uint16int32uint32float32complex64int64uint64float64complex128还有uintptr,一个大小足够存储一个指针的数值。这些都是互不相同的类型;int不等于是int32,即便是在一个32位的机器上。没有隐式类型转换(不过不要恐慌)。Bool普通的布尔类型bool,取值true和false(预定义的常量)。if语句等使用布尔表达式。指针类型和整型不是布尔类型。string原生内置的string类型代表不可改变的字节数组,即文本。string类型是用长度定界的,而不是以结尾0终止的。字符串字面值是string类型。和整型一样不可改变。可重新赋值,但不能修改其值。正如3总是3,hello也总是hello。Go语言对字符串操作提供了良好的支持。表达式(Expressions)大多都是类C语言的操作符。二元操作符:优先级操作符备注5*/%&&^&^是位清理操作符4+–|^^是异或(xor)3==!===2&&1||一元操作符包括:&!*+–^(外加用于通信的-)一元操作符^是求补码/反码操作。Govs.C表达式可以让C程序员惊喜的是:更少的优先级层次(应该容易)。^替代了~++和–不再是表达式操作符(x++是一个语句,不是表达式;*p++是(*p)++,而不是*(p++))&^是新操作符,在常量表达式中很有用和等需要一个无符号的移位计数。无惊喜的是:赋值操作与所期望的一样:+==&^=等表达式总体看起来相似(下标、函数调用等)例子+x23+3*x[i]x=f()^abf()||g()x==y+1&&-ch0x&^7//xwiththelow3bitsclearedfmt.Printf(%5.2g\n,2*math.Sin(PI/8))7.234/x+2.3ihello,+world//concatenation//noC-likeab数值转型将一个数值从一个类型转换为另一个类型称为一次转型,其语法形式有点类似函数调用:uint8(intVar)//截断到相应的大小int(float64Var)//片段截断float64(intVar)//转为float64一些涉及string类型的转型:string(0×1234)//==\u1234string(sliceOfBytes)//bytes-bytesstring(sliceOfInts)//ints-Unicode/UTF-8[]byte(abc)//bytes-bytes[]int(日本語)//Unicode/UTF-8-ints切片(slice)与数组相关,稍后会有更多相关内容。常量数值常量是理想数:没有大小或标志,因此没有U、L或UL作结尾。077//八进制0xFEEDBEEEEEEEEEEEEEEEEEEEEF//十六进制1100下面是整数和浮点数值,字面值的语法决定其类型:1.234e5//浮点1e2//浮点3.2i//浮点虚数100//整数常量表达式浮点和整型常量可以任意组合,最终表达式的类型由常量的类型决定。操作自身也取决于类型。2*3.14//浮点:6.283./2//浮点:1.53/2//整型:13+2i//复数:3.0+2.0i//高精度constLn2=0.69314718055994530941723212145817656807constLog2E=1/Ln2数值的表示范围足够大(目前最大用1024位表示)。理想数的结果Go语言允许无需显式转型的情况下使用常量,前提是常量值可以被其类型表示(没有必要进行转型;其值表示起来没问题):varmillionint=1e6//float语法在这里可以使用math.Sin(1)常量必须可以被其类新表示。例如:^0的值为-1,不在0-255的范围内。uint8(^0)//错误:-1无法用uint8类型表示^uint8(0)//OKuint8(350)//错误:350无法用uint8类型表示uint8(35.0)//OK:35uint8(3.5)//错误:3.5无法用uint8类型表示声明声明以一个关键字开头(var,const,type和func),并且与C中的声明次序相反:variintconstPI=22./7.typePointstruct{x,yint}funcsum(a,bint)int{returna+b}为何要以相反次序声明呢?早期的一个例子:varp,q*intp和q的类型都是*int。并且函数读起来更佳,并且与其他声明一致。还有一个原因,马上道来。Var变量声明以var开头。它们可以有一个类型或一个初始化表达式;至少应有一个或二者都有。初始化表达式应该与变量匹配(还有类型!)。variintvarj=365.245varkint=0varl,muint64=1,2varnanosecondsint64=1e9//float64constant!varinter,floater,stringer=1,2.0,hi分派var总是输入var让人生厌。我们可以通过括号让多个变量声明成为一组:var(iintj=356.245kint=0l,muint64=1,2nanosecondsint64=1e9inter,floater,stringer=1,2.0,hi)这种形式适用于const,type,var,但不能用于func。=:短声明在函数内(只有在函数内这一种情况下),下面形式的声明:varv=value可以被缩短成:v:=value(这就是另外一个名字、类型倒序的原因)类型就是值的类型(对于理想数,相应的类型是int或float64或complex128)a,b,c,d,e:=1,2.0,three,FOUR,5e0i这种形式的声明使用很频繁,并且在诸如for循环初始化表达式中也可以使用。Const常量声明以const开头。它们必须有一个常量表达式,可在编译期间求值,作为初始化表达式,可以拥有一个可选的类型修饰符。constPi=22./7.constAccuratePifloat64=355./113constbeef,two,parsnip=meat,2,vegconst(Monday,Tuesday,Wednesday=1,2,3Thursday,Friday,Saturday=4,5,6)Iota常量声明可以使用计数器:iota,每个const块中的iota都从0开始计数,在每个隐式的分号(行尾)自增。const(Monday=iota//0Tuesday=iota//1)速记:重复上一个类型和表达式。const(loc0,bit0uint32=iota,1loc1,bit1//1,2loc2,bit2//2,4)Type类型声明以type开头。我们后续会学习更多类型,不过先这里举几个例子:typePointstruct{x,y,zfloat64namestring}typeOperatorfunc(a,bint)inttypeSliceOfIntPointers[]*int我们稍后会回到函数。New内置函数new用于分配内存。其语法类似一个函数调用,以类型作为参数,与C++中的new类似。返回一个指向已分配对象的指针。varp*Point=new(Point