Golang入门——第一个程序Hello Golang

Hello Golang

package main
import "fmt"// 我们需要使用fmt包中的Println()函数
func main() {
fmt.Println("Hello, Golang. 你好,Golang! ")
}

1,Golang语法解读

每个Go源代码文件的开头都是一个package声明,表示该Go代码所属的包。包是Go语言里最基本的分发单位,也是工程管理中依赖关系的体现。要生成Go可执行程序,必须建立一个名字为main的包,并且在该包中包含一个叫main()的函数(该函数是Go可执行程序的执行起点)。Go语言的main()函数不能带参数,也不能定义返回值。命令行传入的参数在os.Args变量中保存。如果需要支持命令行开关,可使用flag包。在本书后面我们将解释如何使用flag包来做命令行参数规范的定义,以及获取和解析命令行参数。
在包声明之后,是一系列的import语句,用于导入该程序所依赖的包。由于本示例程序用
到了Println()函数,所以需要导入该函数所属的fmt包。这个包是对一些打印流的封装
所有Go函数(包括在对象编程中会提到的类型成员函数)以关键字func开头。一个常规的
函数定义包含以下部分:
func 函数名(参数列表)(返回值列表) {
// 函数体
}
对应的一个实例如下:
func Compute(value1 int, value2 float64)(result float64, err error) {
// 函数体
}
Go支持多个返回值。以上的示例函数Compute()返回了两个值,一个叫result,另一个是
err。并不是所有返回值都必须赋值。在函数返回时没有被明确赋值的返回值都会被设置为默认
值,比如result会被设为0.0, err会被设为nil。err用来返回错误信息
Golang的注释语法与java,C相似,
与C++保持一致,即同时支持以下两种用法:
/*
块注释
*/
// 行注释

**相信熟悉C和C++的读者也发现了另外一点,即在这段Go示例代码里没有出现分号。 Go
程序并不要求开发者在每个语句后面加上分号表示语句结束,这是与C和C++的一个明显不同
之处。
有些读者可能会自然地把左花括号{另起一行放置,这样做的结果是Go编译器报告编译错
误,这点需要特别注意:
syntax error: unexpected semicolon or newline before {**

2,编译环境

因为大天朝的高墙,有些读者可能没有翻墙工具无法访问 Golang官方下载地址:https://code.google.com/archive/p/go/downloads,所以博主给大家准备了一个翻墙神器,作为做开发的翻墙工具 和VPN应该是必备的, lantern下载地址,以及下载之后的配置问题 博主就不在博文中说了,Golang安装及配置 Golang 配置方法

3,编译程序

假设之前介绍的Hello, world代码被保存为了hello.go,并位于~/goyard目录下,那么可以用以
下命令行编译并直接运行该程序:
cd /goyard go run hello.go # 直接运行
Hello, world. 你好,世界!
使用这个命令,会将编译、链接和运行3个步骤合并为一步,运行完后在当前目录下也看不
到任何中间文件和最终的可执行文件。如果要只生成编译结果而不自动运行,我们也可以使用 Go
命令行工具的build命令:
cd /goyard go build hello.go
$ ./hello
Hello, world. 你好,世界!
可以看出, Go命令行工具是一个非常强大的源代码管理工具。我们将在第4章中详细讲解Go
命令行工具所包含的更多更强大的功能。
从根本上说, Go命令行工具只是一个源代码管理工具,或者说是一个前端。真正的Go编译

器和链接器被Go命令行工具隐藏在后面,我们可以直接使用它们:
6ghelloworld.go 6l helloworld.6
$ ./6.out
Hello, world. 你好,世界!
6g和6l是64位版本的Go编译器和链接器,对应的32位版本工具为8g和8l。 Go还有另外一个
GCC版本的编译器,名为 gccg,这就不详细说了

4, IDE的选择

Google并没有随着Go 1的发布推出官方的Go集成开发环境(IDE) ,因此开发者需要自行考
虑和选择合适的开发工具。目前比较流行的开发工具如下:

  • 文本编辑工具gedit(Linux) /Notepad++(Windows) /Fraise(Mac OS X);
  • 安装了GoClipse插件的Eclipse,集成性做得很好;
  • Vim/Emacs,万能开发工具;
  • LiteIDE,一款专为Go语言开发的集成开发环境。
    由于Go代码的轻巧和模块化特征,其实一般的文本编辑工具就可以胜任Go开发工作。所以博主的用是Sublime3来编写Golang代码

5,工程管理

在实际的开发工作中,直接调用编译器进行编译和链接的场景是少而又少,因为在工程中不
会简单到只有一个源代码文件,且源文件之间会有相互的依赖关系。如果这样一个文件一个文件
逐步编译,那不亚于一场灾难。 Go语言的设计者作为行业老将,自然不会忽略这一点。早期Go
语言使用makefile作为临时方案,到了Go 1发布时引入了强大无比的Go命令行工具。
Go命令行工具的革命性之处在于彻底消除了工程文件的概念,完全用目录结构和包名来推
导工程结构和构建顺序。针对只有一个源文件的情况讨论工程管理看起来会比较多余,因为这可
以直接用go run和go build搞定。下面我们将用一个更接近现实的虚拟项目来展示Go语言的
基本工程管理方法。
假设有这样一个场景:我们需要开发一个基于命令行的计算器程序。下面为此程序的基本

用法:
$ calc help
USAGE: calc command [arguments] ...
The commands are:
sqrt Square root of a non-negative value.
add Addition of two values.
$ calc sqrt 4 # 开根号
2
$ calc add 1 2 # 加法
3

我们假设这个工程被分割为两个部分:

  • 可执行程序,名为calc,内部只包含一个calc.go文件;
  • 算法库,名为simplemath,每个command对应于一个同名的go文件,比如add.go。
    则一个正常的工程目录组织应该如下所示:

    ├─
    ├─
    ├─calc.go
    ├─
    ├─add.go
    ├─add_test.go
    ├─sqrt.go
    ├─sqrt_test.go
    ├─
    ├─#包将被安装到此处
    在上面的结构里,带尖括号的名字表示其为目录。 xxx_test.go表示的是一个对于xxx.go的单元
    测试,这也是Go工程里的命名规则。
package main
import "os"// 用于获得命令行参数os.Args
import "fmt"
import "simplemath"
import "strconv"
var Usage = func() {
fmt.Println("USAGE: calc command [arguments] ...")
fmt.Println("\nThe commands are:\n\tadd\tAddition of two values.\n\tsqrt\tSquare

root of a non-negative value.")
}
func main() {
args := os.Args
if args == nil || len(args) < 2 {
Usage()
return
}
switch args[0] {
case "add":
if len(args) != 3 {
fmt.Println("USAGE: calc add <integer1><integer2>")
return
}
v1, err1 := strconv.Atoi(args[1])
v2, err2 := strconv.Atoi(args[2])
if err1 != nil || err2 != nil {
fmt.Println("USAGE: calc add <integer1><integer2>")
return
}
ret := simplemath.Add(v1, v2)
fmt.Println("Result: ", ret)
case "sqrt":
if len(args) != 2 {
fmt.Println("USAGE: calc sqrt <integer>")
return
}
v, err := strconv.Atoi(args[1])
if err != nil {
fmt.Println("USAGE: calc sqrt <integer>")
return
}
ret := simplemath.Sqrt(v)
fmt.Println("Result: ", ret)
default:
Usage()
}
}

add.go

// add.go
package simplemath
func Add(a int, b int) int {
return a + b
}

add_test.go

// add_test.go
package simplemath
import "testing"
func TestAdd1(t *testing.T) {
r := Add(1, 2)
if r != 3 {
t.Errorf("Add(1, 2) failed. Got %d, expected 3.", r)
}
}

sqrt.go

// sqrt.go
package simplemath
import "math"
func Sqrt(i int) int {
v := math.Sqrt(float64(i))
return int(v)
}

sqrt_test.go

// sqrt_test.go
package simplemath
import "testing"
func TestSqrt1(t *testing.T) {
v := Sqrt(16)
if v != 4 {
t.Errorf("Sqrt(16) failed. Got %v, expected 4.", v)
}
}

为了能够构建这个工程,需要先把这个工程的根目录加入到环境变量GOPATH中。假设calcproj
目录位于~/goyard下,则应编辑~/.bashrc文件,并添加下面这行代码:
export GOPATH=~/goyard/calcproj
然后执行以下命令应用该设置:
$ source ~/.bashrc
GOPATH和PATH环境变量一样,也可以接受多个路径,并且路径和路径之间用冒号分割。

设置完GOPATH后,现在我们开始构建工程。假设我们希望把生成的可执行文件放到
calcproj/bin目录中,需要执行的一系列指令如下:
cd /goyard/calcproj mkdir bin
cdbin go build calc
顺利的话,将在该目录下发现生成的一个叫做calc的可执行文件,执行该文件以查看帮助信
息并进行算术运算:
./calcUSAGE:calccommand[arguments]Thecommandsare:addAdditionoftwovalues.sqrtSquarerootofanonnegativevalue. ./calc add 2 3
Result: 5
$ ./calc sqrt 9
Result: 3
go build calc

这就是为什么说Go命令行工具是非常强大的。我们不需要写makefile,因为这个工具会替我
们分析,知道目标代码的编译结果应该是一个包还是一个可执行文件,并分析import语句以了
解包的依赖关系,从而在编译calc.go之前先把依赖的simplemath编译打包好。 Go命令行程序制
定的目录结构规则让代码管理变得非常简单。
另外,我们在写simplemath包时,为每一个关键的函数编写了对应的单元测试代码,分别
位于add_test.go和sqrt_test.go中。那么我们到底怎么运行这些单元测试呢?这也非常简单。因为
已经设置了GOPATH,所以可以在任意目录下执行以下命令:
$ go test simplemath
ok simplemath0.014s

可以看到,运行结果列出了测试的内容、测试结果和测试时间。如果我故意把add_test.go的
代码改成这样的错误场景:
func TestAdd1(t *testing.T) {
r := Add(1, 2)
if r != 2 { // 这里本该是3,故意改成2测试错误场景
t.Errorf(“Add(1, 2) failed. Got %d, expected 3.”, r)
}
}
然后我们再次执行单元测试,将得到如下的结果:
$ go test simplemath
— FAIL: TestAdd1 (0.00 seconds)
add_test.go:8: Add(1, 2) failed. Got 3, expected 3.
FAIL
FAILsimplemath0.013s
打印的错误信息非常简洁,却已经足够让开发者快速定位到问题代码所在的文件和行数,从
而在最短的时间内确认是单元测试的问题还是程序的问题。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章