18第1章初识Go语言1.6问题追踪和调试Go语言所提供的是尽量简单的语法和尽量完善的库,以尽可能降低问题的发生概率。当然,问题还是会发生的,这时需要用到问题追踪和调试技能。这单我们简单介绍下两个最常规的问题跟踪方法:打印日志和使用GDB进行逐步调试。1.6.1打印日志Go语言包中包含一个fmt包,其中提供了大量易用的打印函数,我们会接触到的主要是Printf()和Print1n()。这两个函数可以满足我们的基本调试需求,比如临时打印某个变量。这两个函数的参数非常类似于C语言运行库中的Printf(),有C语言开发经验的同学会很容易上手。下面是几个使用Printf()和Println()的例子:fval := 110.48ival := 200sval:="This isa string."fmt.Println("The value of fval is", fval)fmt.printf("fval=gf,ival-gd,sval=gs\n',fval,ival,sval)fmt.Printf("fval=gv,ival-gv,sval=vln",fval,ival,sval)输出结果为:The value of fval is 100.48fval=100.48,ival=200,sval=This is a stringfval=100.48,ival=200,sval=This is a stringfmt包的这一系列格式化打印函数使用起来非常方便,但在正式开始用Go开发服务器系统时,我们就不能只依赖fmt包了,而是需要设计严格的日志规范。Go语言的1og包提供了基础的日志功能。如果有需要,你也可以引人自己的1og模块。1.6.2GDB调试不用设置什么编译选项,Go语言编译的二进制程序直接支持GDB调试,比如之前用gobui1dca1c编译出来的可执行文件calc,就可以直接用以下命令以调试模式运行:$ gdb calc因为GDB的标准用法与Go没有特别关联,这里就不详细展开了,有兴趣的读者可以自行查看对应的文档。需要注意的是,Go编译器生成的调试信息格式为DWARFv3,只要版本高于7.1的GDB应该都支持它。1.7如何寻求帮助Go语言已经发展了两年时间,凭借着语言本身的优越品质和Google的强大号召力,在推出正式版本之前就已经拥有了广大的爱好者和社区,本节就介绍一些不错的Go语言社区。在遇到问图灵社区会员soooldier(soooldier@live.com)专享尊重版权
18 第 1 章 初识 Go 语言 1.6 问题追踪和调试 Go语言所提供的是尽量简单的语法和尽量完善的库,以尽可能降低问题的发生概率。当然, 问题还是会发生的,这时需要用到问题追踪和调试技能。这里我们简单介绍下两个最常规的问题 跟踪方法:打印日志和使用GDB进行逐步调试。 1.6.1 打印日志 Go语言包中包含一个fmt包,其中提供了大量易用的打印函数,我们会接触到的主要是 Printf()和Println()。这两个函数可以满足我们的基本调试需求,比如临时打印某个变量。 这两个函数的参数非常类似于C语言运行库中的Printf(),有C语言开发经验的同学会很容易上 手。下面是几个使用Printf()和Println()的例子: fval := 110.48 ival := 200 sval := "This is a string. " fmt.Println("The value of fval is", fval) fmt.Printf("fval=%f, ival=%d, sval=%s\n", fval, ival, sval) fmt.Printf("fval=%v, ival=%v, sval=%v\n", fval, ival, sval) 输出结果为: The value of fval is 100.48 fval=100.48, ival=200, sval=This is a string. fval=100.48, ival=200, sval=This is a string. fmt包的这一系列格式化打印函数使用起来非常方便,但在正式开始用Go开发服务器系统 时,我们就不能只依赖fmt包了,而是需要设计严格的日志规范。Go语言的log包提供了基础的 日志功能。如果有需要,你也可以引入自己的log模块。 1.6.2 GDB调试 不用设置什么编译选项,Go语言编译的二进制程序直接支持GDB调试,比如之前用go build calc编译出来的可执行文件calc,就可以直接用以下命令以调试模式运行: $ gdb calc 因为GDB的标准用法与Go没有特别关联,这里就不详细展开了,有兴趣的读者可以自行查 看对应的文档。需要注意的是,Go编译器生成的调试信息格式为DWARFv3,只要版本高于7.1 的GDB应该都支持它。 1.7 如何寻求帮助 Go语言已经发展了两年时间,凭借着语言本身的优越品质和Google的强大号召力,在推出正 式版本之前就已经拥有了广大的爱好者和社区,本节就介绍一些不错的Go语言社区。在遇到问 图灵社区会员 soooldier(soooldier@live.com) 专享 尊重版权
1.8小结19题时,请随时访问这些社区,并勇敢地提问,相信你能得到满意的解决方法。1.7.1邮件列表邮件列表是Go语言最活跃的社区之一,而且与其他语言社区不同的是,在这里你可以很频繁地看到好多Go语言的核心开发成员(比如RossCox)亲自回答问题,其权威程度和对学习Go语言的价值显而易见。Go邮件组的地址为http://groups.google.com/group/golang-nuts。该邮件列表对所有人公开,你可以在这个页面上直接加入。该邮件列表的沟通语言为英语。根据我们的经验,在该邮件列表上提出的问题通常在24小时内可以得到解决。Go的中文邮件组为http://groups.google.com/group/golang-china。如果你更习惯中文讨论环境,可以参与。另外,尽管http://groups.google.com/group/ecug不是以Go语言为专题,但有关Go语言的服务端开发,也是它最重要的话题之一。1.7.2网站资源Go语言的官方网站为http://golang.org,这个网站只随着Go的主要版本发布而更新,因此并不反映Go的最新进展。如果读者希望跟进Go语言的最新进展,可以到http:/code.google.com/p/go/直接下载最新代码。这里持续对Go资料进行了整理:http://github.com/wonderfo/wonderfogo/wiki。1.8小结本章我们简要介绍了Go语言的起源和背景,并结合若干代码示例简要介绍了我们认为最值得关注的关键特性,之后按老规矩以Hello,World这个例子作为起点帮助读者快速熟悉这门新语言,消除对Go语言的陌生感,并搭建好自己的Go开发环境。通过这一章的学习,我们相信读者对于Go语言的简单易学特性已经有了比较直接的了解。在后续的章节中,各位读者可以利用在本章中搭建的开发环境和学习的工程管理知识,快速动手尝试各种Go语言令人兴奋的语言功能。图灵社区会员soooldier(soooldier@live.com)专享尊重版权
1.8 小结 19 1 2 3 4 5 9 6 7 8 2 题时,请随时访问这些社区,并勇敢地提问,相信你能得到满意的解决方法。 1.7.1 邮件列表 邮件列表是Go语言最活跃的社区之一,而且与其他语言社区不同的是,在这里你可以很频 繁地看到好多Go语言的核心开发成员(比如Ross Cox)亲自回答问题,其权威程度和对学习Go 语言的价值显而易见。 Go邮件组的地址为http://groups.google.com/group/golang-nuts 。该邮件列表对所有人公开,你 可以在这个页面上直接加入。该邮件列表的沟通语言为英语。根据我们的经验,在该邮件列表上 提出的问题通常在24小时内可以得到解决。 Go的中文邮件组为http://groups.google.com/group/golang-china。如果你更习惯中文讨论环境, 可以参与。另外,尽管http://groups.google.com/group/ecug不是以Go语言为专题,但有关Go语言 的服务端开发,也是它最重要的话题之一。 1.7.2 网站资源 Go语言的官方网站为 http://golang.org,这个网站只随着Go的主要版本发布而更新,因此并 不反映Go的最新进展。如果读者希望跟进Go语言的最新进展,可以到http://code.google.com/p/go/ 直接下载最新代码。这里持续对Go资料进行了整理:http://github.com/wonderfo/wonderfogo/wiki。 1.8 小结 本章我们简要介绍了Go语言的起源和背景,并结合若干代码示例简要介绍了我们认为最值 得关注的关键特性,之后按老规矩以Hello, world这个例子作为起点帮助读者快速熟悉这门新语 言,消除对Go语言的陌生感,并搭建好自己的Go开发环境。 通过这一章的学习,我们相信读者对于Go语言的简单易学特性已经有了比较直接的了解。 在后续的章节中,各位读者可以利用在本章中搭建的开发环境和学习的工程管理知识,快速动手 尝试各种Go语言令人兴奋的语言功能。 图灵社区会员 soooldier(soooldier@live.com) 专享 尊重版权
第2章顺序编程从本章开始,我们将为你逐步展开Go语言的各种美妙特性,而本章主要介绍Go语言的顺序编程特性。在阅读完本章后,相信你会理解为什么Go语言会被称为“更好的C语言”。在本章中我们会自然涉及一些C语言的知识。如果你之前没有学过C语言,也没关系,对于Go语言的整体理解并不会有太大的影响,但如果之前学过C语言,那么你将会更具体地理解Go语言相比C语言的众多革新之处。2.1变量变量是几乎所有编程语言中最基本的组成元素。从根本上说,变量相当于是对一块数据存储空间的命名,程序可以通过定义一个变量来申请一块数据存储空间,之后可以通过引用变量名来使用这块存储空间。Go语言中的变量使用方式与C语言接近,但具备更大的灵活性。2.1.1变量声明Go语言的变量声明方式与C和C++语言有明显的不同。对于纯粹的变量声明,Go语言引入了关键字var,而类型信息放在变量名之后,示例如下:var vi intvar v2 string//数组var v3[10]int//数组切片var v4 []intvar v5 struct (f int1/指针var v6 *intvar v7map[string]int//map,key为string类型,value为int类型var v8 func(aint) int变量声明语句不需要使用分号作为结束符。与C语言相比,Go语言据弃了语句必须以分号作为语句结束标记的习惯。var关键学的另一种用法是可以将若干个需要声明的变量放置在一起,免得程序员需要重复写var关键字,如下所示:图灵社区会员soooldier(soooldier@live.com)专享尊重版权
20 第 2 章 顺序编程 顺 序 编 程 从本章开始,我们将为你逐步展开Go语言的各种美妙特性,而本章主要介绍Go语言的顺序 编程特性。在阅读完本章后,相信你会理解为什么Go语言会被称为“更好的C语言”。 在本章中我们会自然涉及一些C语言的知识。如果你之前没有学过C语言,也没关系,对于 Go语言的整体理解并不会有太大的影响,但如果之前学过C语言,那么你将会更具体地理解Go 语言相比C语言的众多革新之处。 2.1 变量 变量是几乎所有编程语言中最基本的组成元素。从根本上说,变量相当于是对一块数据存储 空间的命名,程序可以通过定义一个变量来申请一块数据存储空间,之后可以通过引用变量名来 使用这块存储空间。 Go语言中的变量使用方式与C语言接近,但具备更大的灵活性。 2.1.1 变量声明 Go语言的变量声明方式与C和C++语言有明显的不同。对于纯粹的变量声明,Go语言引入了 关键字var,而类型信息放在变量名之后,示例如下: var v1 int var v2 string var v3 [10]int // 数组 var v4 []int // 数组切片 var v5 struct { f int } var v6 *int // 指针 var v7 map[string]int // map,key为string类型,value为int类型 var v8 func(a int) int 变量声明语句不需要使用分号作为结束符。与C语言相比,Go语言摒弃了语句必须以分号作 为语句结束标记的习惯。 var关键字的另一种用法是可以将若干个需要声明的变量放置在一起,免得程序员需要重复 写var关键字,如下所示: 第2 章 图灵社区会员 soooldier(soooldier@live.com) 专享 尊重版权
2.1变量21var(vlintv2 string)2.1.2变量初始化2对于声明变量时需要进行初始化的场景,var关键字可以保留,但不再是必要的元素,如下所示:var v1int=10//正确的使用方式11/正确的使用方式2,编译器可以自动推导出v2的类型varv2=10//正确的使用方式3,编译器可以自动推导出v3的类型v3:= 10以上三种用法的效果是完全一样的。与第一种用法相比,第三种用法需要输入的字符数大大减少,是懒程序员和聪明程序员的最佳选择。这里Go语言也引入了另一个C和C++中没有的符号(冒号和等号的组合:=),用于明确表达同时进行变量声明和初始化的工作。指定类型已不再是必需的,Go编译器可以从初始化表达式的右值推导出该变量应该声明为哪种类型,这让Go语言看起来有点像动态类型语言,尽管Go语言实际上是不折不扣的强类型语言(静态类型语言)。当然,出现在:=左侧的变量不应该是已经被声明过的,否则会导致编译错误,比如下面这个写法:var i inti:=2会导致类似如下的编译错误:no new variables on left side of :=2.1.3变量赋值在Go语法中,变量初始化和变量赋值是两个不同的概念。下面为声明一个变量之后的赋值过程:varvi0 intv10=123Go语言的变量赋值与多数语言一致:但Go语言中提供了C/C++程序员期多年的多重赋值功能,比如下面这个交换i和变量的语句:i,j=j,i在不支持多重赋值的语言中,交互两个变量的内容需要引人一个中间变量:t=i;i=j;j=t;图灵社区会员soooldier(soooldier@live.com)专享尊重版权
2.1 变量 21 1 2 3 4 5 9 6 7 8 var ( v1 int v2 string ) 2.1.2 变量初始化 对于声明变量时需要进行初始化的场景,var关键字可以保留,但不再是必要的元素,如下 所示: var v1 int = 10 // 正确的使用方式1 var v2 = 10 // 正确的使用方式2,编译器可以自动推导出v2的类型 v3 := 10 // 正确的使用方式3,编译器可以自动推导出v3的类型 以上三种用法的效果是完全一样的。与第一种用法相比,第三种用法需要输入的字符数大大 减少,是懒程序员和聪明程序员的最佳选择。这里Go语言也引入了另一个C和C++中没有的符号 (冒号和等号的组合:=),用于明确表达同时进行变量声明和初始化的工作。 指定类型已不再是必需的,Go编译器可以从初始化表达式的右值推导出该变量应该声明为 哪种类型,这让Go语言看起来有点像动态类型语言,尽管Go语言实际上是不折不扣的强类型语 言(静态类型语言)。 当然,出现在:=左侧的变量不应该是已经被声明过的,否则会导致编译错误,比如下面这个 写法: var i int i := 2 会导致类似如下的编译错误: no new variables on left side of := 2.1.3 变量赋值 在Go语法中,变量初始化和变量赋值是两个不同的概念。下面为声明一个变量之后的赋值 过程: var v10 int v10 = 123 Go语言的变量赋值与多数语言一致,但Go语言中提供了C/C++程序员期盼多年的多重赋值功 能,比如下面这个交换i和j变量的语句: i, j = j, i 在不支持多重赋值的语言中,交互两个变量的内容需要引入一个中间变量: t = i; i = j; j = t; 图灵社区会员 soooldier(soooldier@live.com) 专享 尊重版权
22第2章顺序编程多重赋值的特性在Go语言库的实现中也被使用得相当充分,在介绍函数的多重返回值时,将对其进行更加深入的介绍。总而言之,多重赋值功能让Go语言与C/C++语言相比可以非常明显地减少代码行数。2.1.4匿名变量我们在使用传统的强类型语言编程时,经常会出现这种情况,即在调用函数时为了获取一个值,却因为该函数返回多个值而不得不定义一堆没用的变量。在Go中这种情况可以通过结合使用多重返回和匿名变量来避免这种丑陋的写法,让代码看起来更加优雅。假设GetName()函数的定义如下,它返回3个值,分别为firstName、lastName和nickName:func GetName() (firstName, lastName, nickName string) freturn"May","Chan",""Chibi Maruko')若只想获得nickName,则函数调用语句可以用如下方式编写:-" -- nickName := GetName()这种用法可以让代码非常清晰,基本上屏蔽掉了可能混淆代码阅读者视线的内容,从而大幅降低沟通的复杂度和代码维护的难度。2.2常量在Go语言中,常量是指编译期间就已知且不可改变的值。常量可以是数值类型(包括整型浮点型和复数类型)、布尔类型、学符串类型等。2.2.1字面常量所谓字面常量(literal),是指程序中硬编码的常量,如:-121 / 浮点类型的常量3.14159265358979323846/ /复数类型的常量3.2+12i//布尔类型的常量true//字符串常量"foo"在其他语言中,常量通常有特定的类型,比如-12在C语言中会认为是一个int类型的常量。如果要指定一个值为-12的1ong类型常量,需要写成-12l,这有点违反人们的直观感觉。Go语言的字面常量更接近我们自然语言中的常量概念,它是无类型的。只要这个常量在相应类型的值域范围内,就可以作为该类型的常量,比如上面的常量-12,它可以赋值给int、uint、int32、int64、f1oat32、f1oat64、complex64、complex128等类型的变量。图灵社区会员soooldier(soooldier@live.com)专享尊重版权
22 第 2 章 顺序编程 多重赋值的特性在Go语言库的实现中也被使用得相当充分,在介绍函数的多重返回值时, 将对其进行更加深入的介绍。总而言之,多重赋值功能让Go语言与C/C++语言相比可以非常明显 地减少代码行数。 2.1.4 匿名变量 我们在使用传统的强类型语言编程时,经常会出现这种情况,即在调用函数时为了获取一个 值,却因为该函数返回多个值而不得不定义一堆没用的变量。在Go中这种情况可以通过结合使 用多重返回和匿名变量来避免这种丑陋的写法,让代码看起来更加优雅。 假 设GetName()函数的定义如下,它返回3个值,分别为firstName、lastName和 nickName: func GetName() (firstName, lastName, nickName string) { return "May", "Chan", "Chibi Maruko" } 若只想获得nickName,则函数调用语句可以用如下方式编写: _, _, nickName := GetName() 这种用法可以让代码非常清晰,基本上屏蔽掉了可能混淆代码阅读者视线的内容,从而大幅 降低沟通的复杂度和代码维护的难度。 2.2 常量 在Go语言中,常量是指编译期间就已知且不可改变的值。常量可以是数值类型(包括整型、 浮点型和复数类型)、布尔类型、字符串类型等。 2.2.1 字面常量 所谓字面常量(literal),是指程序中硬编码的常量,如: -12 3.14159265358979323846 // 浮点类型的常量 3.2+12i // 复数类型的常量 true // 布尔类型的常量 "foo" // 字符串常量 在其他语言中,常量通常有特定的类型,比如12在C语言中会认为是一个int类型的常量。 如果要指定一个值为12的long类型常量,需要写成12l,这有点违反人们的直观感觉。Go语言 的字面常量更接近我们自然语言中的常量概念,它是无类型的。只要这个常量在相应类型的值域 范围内,就可以作为该类型的常量,比如上面的常量12,它可以赋值给int、uint、int32、 int64、float32、float64、complex64、complex128等类型的变量。 图灵社区会员 soooldier(soooldier@live.com) 专享 尊重版权