Go 编程语言入门教程
原文:http://golang.org
翻译:刘金雨/刘云涛 <yuntao.liu#gmail.com> http://www.log4think.com
目录
介绍
本文档是关于Go编程语言基础的一个介绍性的入门教程,偏向于熟悉C或C++的读者。本文并非一份语言的完整指南,如果需要的话,你应该去看看“语言规范”(language specification)。当读完本教程之后,你可能会希望继续看看“Effective Go”,这份文档更深入的挖掘如何使用Go语言。此外还有一份《三日入门》的教程可供参考: 第一日,第二日, 第三日.
本文将会以一系列适当的程序来说明语言的一些关键特性。所有的示例程序都是可运行的(在撰写本文时),并且这些程序都会提交到版本库的/doc/progs/目录下。
程序片段都会标注上在源文件中的行号,为了清晰起见,空行前面的行号留空。
Hello, World
让我们以最常见的方式开始吧:
05 package main
07 import fmt "fmt" // 本包实现了格式化输入输出
09 func main() {
10 fmt.Printf("Hello, world; or Καλημέρα κόσμε; or こんにちは 世界/n");
11 }
每份Go的源文件都会使用 package 语句声明它的包名。同时也可以通过导入其它包来使用其中定义的功能。这段代码导入了包 fmt 来调用我们的老朋友——现在它的开头字母是大写的,并且前面带有包名限定——fmt.Printf。
函数的声明使用关键字func ,整个程序将会从为main 包中的main 函数开始(经过初始化之后)。
字符串常量可以包含Unicode字符,采用UTF-8编码。(事实上,所有Go程序的源文件都是使用UTF-8编码的)
注释的方式同C++一样:
/* ... */
// ...
稍后,我们会继续谈到print。
编译
Go是一个编译型语言。目前有两个编译器,其中gccgo编译器采用了GCC作为后端,此外还有一系列根据其所适用的架构命名的编译器,例如6g 适用于64位的x86结构,8g 适用于32位的x86结构,等等。这些编译器比gccgo运行的更快、生成的代码更加有效率。在撰写本文的时候(2009年底),他们还具有一个更加健壮的运行期系统,但是gccgo 也正在迎头赶上。
下面来看看如何编译和运行程序。采用 6g 是这样的
$ 6g helloworld.go # 编译; 中间代码位于 helloworld.6 中
$ 6l helloworld.6 # 链接; 输出至 6.out
$ 6.out
Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
$
gccgo 的方式看起来更加传统一些。
$ gccgo helloworld.go
$ a.out
Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
$
Echo
下一步,是来实现一个Unix的传统命令Echo:
05 package main
07 import (
08 "os";
09 "flag"; // command line option parser
10 )
12 var omitNewline = flag.Bool("n", false, "don't print final newline")
14 const (
15 Space = " ";
16 Newline = "/n";
17 )
19 func main() {
20 flag.Parse(); // Scans the arg list and sets up flags
21 var s string = "";
22 for i := 0; i < flag.NArg(); i++ {
23 if i > 0 {
24 s += Space
25 }
26 s += flag.Arg(i);
27 }
28 if !*omitNewline {
29 s += Newline
30 }
31 os.Stdout.WriteString(s);
32 }
这段程序很小,但是却有几个特别之处。前面这个例子中,我们看到可以使用func 来声明一个函数,同时关键字 var、const和type (目前还没有用到)也可以用于声明,就好像 import 一样。注意,我们可以将同一类的声明放到括号中,以分号分隔,例如第7-10行和第14-17行。但也并非一定要如此,例如可以这样写
const Space = " "
const Newline = "/n"
分号在此处并不是必须的。事实上,任何顶层声明后面都不需要分号。但如果要是在一个括号内进行一系列的声明,就需要用分号来分割了。
你可以像在C、C++或Java中那样去使用分号,但如果你喜欢的话,在很多情况下都可以省略掉分号。分号是用于表示语句间的分隔,而非表示其中止。因此对于一个代码块中的最后一条语句来说,有无分号皆可。大括号之后的分号也是可选的,就像C语言中的一样。比对一下echo的源代码,只有第8、15和21行必须要加分号,当然第22行中的 for 语句中为了分隔三个表达式也需要加分号。第9、16、26和31行的分号都不是必须的,加上分号只是为了以后再增加语句的时候方便而已。
这个程序导入了os包以访问 Stdout 变量,Stdout的类型是 *os.File 。import 语句实际上是个声明:通常情况下(如hello world程序中那样),它命名了一个标识符fmt用于访问导入的包的成员变量,而包是从当前目录或标准库下的"fmt"文件中导入的。在这个程序中,我们为导入的包显式的指定了一个名字,默认情况下,包名是采用在导入的包里面已经定义好的名字,通常会与文件名一致。因此在这个“hello world”程序中,可以只写 import "fmt" 。
你可以任意为包指定一个导入名,但通常只有在解决名字冲突的情况下才有必要这样做。
有了 os.Stdout,我们就可以用它的 WriteString 方法打印字符串了。
导入了 flag 包之后,第12行创建了一个全局变量来保存 echo 的 -n 选项标志。omitNewline 变量的类型是 *bool ——指向bool值的指针。
在 main.main中进行了参数解析,并创建了一个本地字符串类型的变量用于构造输出的内容。声明语句如下
var s string = "";
这里用到了关键字 var ,后面跟变量名和数据类型,之后可以继续接=来赋初值。
Go 试图尽量保持简洁,这个声明也可以用更短的形式。因为初值是一个字符串类型的常量,没有必要再声明数据类型了, 因此这个声明可以写成这样:
var s = "";
或者也可以直接用更短的形式:
s := "";
操作符 := 在 Go 语言里经常会用在赋初值的声明中,比如下面这个 for 语句的声明:
22 for i := 0; i < flag.NArg(); i++ {
flag 包会解析命令行参数,并将参数值保存在一个列表中。
Go语言中的 for 语句和C语言中的有几个不同之处。首先,for是唯一的循环语句,没有 while 语句或 do 语句。其次,for 语句后面的三个子句不需要圆括号,但大括号是必须的。这一条对 if 和 switch 语句同样适用。稍后还会有几个例子演示for 语句的其它用法。
循环体中通过追加(+=)标志和空格构造字符串 s 。循环之后,如果没有设置 -n 标志,程序追加一个空行,最后输出结果。
注意,函数 main.main 没有返回值。它就是这样定义的,如果到达了main.main 的末尾就表示“成功”,如果想表明出错并返回,可以调用
os.Exit(1)
os 包还包含一些其他的常用功能,例如 os.Args 会被 flag 包用于访问命令行参数。
(未完待续)