初窥门径:一个Go程序的结构是怎样的?

编写并运行第一个 Go 程序


首先,我们需要创建一个名为 main.go 的源文件。

这里,我需要跟你啰嗦一下 Go 的命名规则。Go 源文件总是用全小写字母形式的短小单词命名,并且以.go 扩展名结尾。

如果要在源文件的名字中使用多个单词,我们通常直接是将多个单词连接起来作为源文件名,而不是使用其他分隔符,比如下划线。也就是说,我们通常使用 helloworld.go 作为文件名而不是 hello_world.go。

这是因为下划线这种分隔符,在 Go 源文件命名中有特殊作用,这个我们会在以后的讲解中详细说明。总的来说,我们尽量不要用两个以上的单词组合作为文件名,否则就很难分辨了。

现在,你可以打开刚刚创建的 main.go 文件,键入下面这些代码:

package main

import "fmt"

func main() {
    fmt.Println("hello, world")
}

写完后,我们保存文件并回到终端窗口,然后在 Linux 或 macOS 系统中,你就可以通过输入下面这个命令来编译和运行这个文件了:

$go build main.go
$./main
hello, world

如果是在 Windows 系统中呢,你需要把上面命令中的./main 替换为.\main.exe。

不过,无论你使用哪种操作系统,到这里你都应该能看到终端输出的“hello, world”字符串了。如果你没有看到这个输出结果,要么是 Go 安装过程的问题,要么是源文件编辑出现了问题,需要你再次认真地确认。如果一切顺利,那么恭喜你!你已经完成了第一个 Go 程序,并正式成为了 Go 开发者!欢迎来到 Go 语言的世界! 

“hello,world”示例程序的结构


现在,让我们回过头来仔细看看“hello,world”示例程序中到底发生了什么。第一个值得注意的部分是这个:

package main

这一行代码定义了 Go 中的一个包 package。包是 Go 语言的基本组成单元,通常使用单个的小写单词命名,一个 Go 程序本质上就是一组包的集合。所有 Go 代码都有自己隶属的包,在这里我们的“hello,world”示例的所有代码都在一个名为 main 的包中。main 包在 Go 中是一个特殊的包,整个 Go 程序中仅允许存在一个名为 main 的包。

main 包中的主要代码是一个名为 main 的函数:

func main() {
    fmt.Println("hello, world")
}

这里的 main 函数会比较特殊:当你运行一个可执行的 Go 程序的时候,所有的代码都会从这个入口函数开始运行。这段代码的第一行声明了一个名为 main 的、没有任何参数和返回值的函数。如果某天你需要给函数声明参数的话,那么就必须把它们放置在圆括号 () 中。

另外,那对花括号{}被用来标记函数体,Go 要求所有的函数体都要被花括号包裹起来。按照惯例,我们推荐把左花括号与函数声明置于同一行并以空格分隔。Go 语言内置了一套 Go 社区约定俗称的代码风格,并随安装包提供了一个名为 Gofmt 的工具,这个工具可以帮助你将代码自动格式化为约定的风格。 

好,回到正题,我们再来看一看 main 函数体中的代码:

fmt.Println("hello, world")

这一行代码已经完成了整个示例程序的所有工作了:将字符串输出到终端的标准输出(stdout)上。不过这里还有几个需要你注意的细节。

注意点 1:标准 Go 代码风格使用 Tab 而不是空格来实现缩进的,当然这个代码风格的格式化工作也可以交由 gofmt 完成。

注意点 2:我们调用了一个名为 Println 的函数,这个函数位于 Go 标准库的 fmt 包中。为了在我们的示例程序中使用 fmt 包定义的 Println 函数,我们其实做了两步操作。

第一步是在源文件的开始处通过 import 声明导入 fmt 包的包路径:

import "fmt"

第二步则是在 main 函数体中,通过 fmt 这个限定标识符(Qualified Identifier)调用 Println 函数。虽然两处都使用了“fmt”这个字面值,但在这两处“fmt”字面值所代表的含义却是不一样的:

  • import “fmt” 一行中“fmt”代表的是包的导入路径(Import),它表示的是标准库下的 fmt 目录,整个 import 声明语句的含义是导入标准库 fmt 目录下的包
  • fmt.Println 函数调用一行中的“fmt”代表的则是包名。 

通常导入路径的最后一个分段名与包名是相同的,这也很容易让人误解 import 声明语句中的“fmt”指的是包名,其实并不是这样的。

main 函数体中之所以可以调用 fmt 包的 Println 函数,还有最后一个原因,那就是 Println 函数名的首字母是大写的。在 Go 语言中,只有首字母为大写的标识符才是导出的(Exported),才能对包外的代码可见;如果首字母是小写的,那么就说明这个标识符仅限于在声明它的包内可见。 

注意点 3:我们还是回到 main 函数体实现上,把关注点放在传入到 Println 函数的字符串“hello, world”上面。你会发现,我们传入的字符串也就是我们执行程序后在终端的标准输出上看到的字符串。

这种“所见即所得”得益于 Go 源码文件本身采用的是 Unicode 字符集,而且用的是 UTF-8 标准的字符编码方式,这与编译后的程序所运行的环境所使用的字符集和字符编码方式是一致的。 

这里,即便我们将代码中的"hello, world"换成中文字符串“你好,世界”,像下面这样:

package main

import "fmt"

func main() {
    fmt.Println("你好,世界")
}

我们依旧可以在终端的标准输出上看到正确的输出。

最后,不知道你有没有发现,我们整个示例程序源码中,都没有使用过分号来标识语句的结束,这与 C、C++、Java 那些传统编译型语言好像不太一样呀?

不过,其实 Go 语言的正式语法规范是使用分号“;”来做结尾标识符的。那为什么我们很少在 Go 代码中使用和看到分号呢?这是因为,大多数分号都是可选的,常常被省略,不过在源码编译时,Go 编译器会自动插入这些被省略的分号。

在分析完这段代码结构后,我们来讲一下 Go 语言的编译。虽然刚刚你应该已经运行过“hello, world”这个示例程序了,在这过程中,有一个重要的步骤——编译,现在我就带你来看看 Go 语言中程序是怎么进行编译的。

猜你喜欢

转载自blog.csdn.net/qq_34556414/article/details/123535600