Go程序源码结构

# Go程序源码结构

我们继续探索 Hello World 程序的奥秘,先来回顾完整的代码:

go
复制代码package main
import "fmt"
func main(){
    fmt.Println("Hello World!")
}

接下来,我们逐行解释代码。

💡 提示: 作为Go语言基础部分,本小节的内容仅对每行代码做功能性解释,不做深入探讨,我们将在后续的章节中详细阐述。例如,对于“main包”,我们暂时只需要对“包”和“main包”有个印象就可以了。
  1. 第一行内容为 “package main”,它表示这个源码文件属于 main 包。main 包是每个 Go 应用程序都包含的包,有且只有一个。
  2. 第二行内容是 “import “fmt"”,它的作用是导入名为 “fmt” 的包,目的在于稍后使用 fmt 包提供的各种能力。一旦某个包被导入,它必须被使用。
  3. 从第三行开始到最后,是 main() 函数,这个函数比较特殊,它是 Go 程序的“入口”函数,是程序运行的起点。这个函数必须存在且只能存在一个,且必须声明在 main 包中。任何一个 Go 函数都要求使用成对的大括号将函数体包裹起来。
  4. 第四行调用了 fmt 包中的 Println() 函数,这个函数的作用是将特定的内容输出到控制台上。
  5. 第五行是 main() 函数的结束。

# 为 Go 源码添加注释

无论使用何种编程语言,为代码添加注释都是必要的。注释的作用是对代码进行解释和说明,目的在于让人们更方便地了解代码。

对于大多数项目,都需要持续一段时间来完成开发和测试,最终上线,上线后还要应对产品的迭代更新。然而,毫不夸张地说,即使仅相隔一周,我们便很可能读不懂代码了,即使这些代码是自己亲手写的。此时,注释就能很好地帮助我们理解这些代码了。

在 Go 语言中,注释分为两类:单行注释和多行注释。

# 单行注释

单行注释也称为行注释,格式为以双斜杠(“//”)开头的一行,可以添加在代码的任何位置。

请大家阅读下面的代码:

go
复制代码package main
import "fmt"
func main(){
   //输出"Hello World!"文字
   fmt.Println("Hello World!")
}

在这段代码中,添加了一行注释,用于解释下方一行代码的作用。

需要注意的是,在使用注释时,不要连同代码一起注释,被注释的代码不会被执行。但在修改代码时,利用这个特性暂时注释掉将被修改的代码,而非直接删除,可以方便在必要时还原它们。

若要添加空白行,可按照如下格式进行:

go
复制代码//第一行
//
//第三行
//第四行

# 多行注释

多行注释也称为块注释,格式为以 “/” 开头,以 “/” 结束的一行或多行。

请大家阅读下面的代码:

go
复制代码/*
main()函数是Go程序的入口函数
是程序运行的起点
此处输出"Hello World!"
用于验证开发环境配置
 */
func main(){
   //输出"Hello World!"文字
   fmt.Println("Hello World!")
}

本例在 main() 函数上方添加了多行注释,用于解释 main() 函数的作用。

和单行注释不同,多行注释不允许嵌套使用,如:

go
复制代码/*
main()函数是Go程序的入口函数
/*是程序运行的起点
此处输出"Hello World!"
*/
用于验证开发环境配置
 */

这将导致编译时错误,无法完成编译。

若要添加空白行,可按照如下格式进行:

go
复制代码/*
第一行

第三行
第四行
 */

《代码大全》一书中有提到:“代码是写给人看的”。养成良好的编码习惯不仅对编码本身有利,对开发团队中的队友也是很好的支持。在实际开发中,人员之间的沟通成本可能会庞大到可怕,请大家谨记。

# Go SDK 命令行工具

Go SDK 提供了丰富且实用的命令行工具,涵盖编译、代码格式化、源码获取、测试和性能分析(这部分内容在单独的章节中阐述)等方方面面,本节将为大家介绍常用的工具。

💡 提示:在后面的章节中,我们会介绍使用集成开发环境(IDE)开发、编译和运行 Go 程序。在 IDE 中可以通过可视化的方式执行 Go SDK 中的命令行工具,但作为基础知识的一环,且在实际开发的某些场景下依然需要通过终端执行这些命令,因此了解并掌握这些命令行工具依然很重要。

# go build

go build 命令的作用是编译 Go 源码,并生成可执行的文件。还记得吗?在上一节中我们已经使用过它了!

从原理上说,Go SDK 自 1.9 版本开始就支持并发编译了,能尽可能地发挥电脑的最大性能完成编译,所以 Go 源码的编译速度是非常快的。在编译过程中,除了我们自己写的代码外,如果使用了第三方的包,这些包会被一同编译。当我们执行 go build 命令后,会搜索当前目录下的 go 源码并完成编译。

go build 命令还允许附加参数,方便开发者对编译参数进行配置,具体如下表所示:

参数名 作用
-v 编译时显示包名
-p x 指定编译时并发的数量(使用x表示),该值默认为CPU的逻辑核心数
-a 强制进行重新构建
-n 仅输出编译时执行的所有命令
-x 执行编译并输出编译时执行的所有命令
-race 开启竞态检测

此外,如果我们希望只编译某个 go 源码文件或包,可在 go build 命令后添加文件或包名。例如,现有 file1.go、file2.go 和file3.go,我们只希望编译 file1.go,便可如下执行:

bash
复制代码go build file1.go

# go clean

go clean 命令可以清理当前目录内的所有编译生成的文件,具体包括:

  • 当前目录下生成的与包名或者 Go 源码文件同名的可执行文件,以及当前目录中 _obj 和 _test 目录中名为 _testmain.go、test.out、build.out、a.out 以及后缀为 .5、.6、.8、.a、.o和 .so 的文件,这些文件通常是执行go build命令后生成的;
  • 当前目录下生成的包名加 “.test” 后缀为名的文件,这些文件通常是执行 go test 命令后生成的;
  • 工作区中 pkg 和 bin 目录的相应归档文件和可执行文件,这些文件通常是执行 go install 命令后生成的。

go clean 命令还允许附加参数,具体参数和作用如下表所示:

参数名 作用
-i 清除关联的安装的包和可运行文件,这些文件通常是执行go install命令后生成的
-n 仅输出清理时执行的所有命令
-r 递归清除在 import 中引入的包
-x 执行清理并输出清理时执行的所有命令
-cache 清理缓存,这些缓存文件通常是执行go build命令后生成的
-testcache 清理测试结果

在团队式开发中,通常在每次提交代码前执行 go clean 命令,防止提交编译时生成的文件。

# go run

go run 命令的作用是直接运行 go 源码,不在当前目录下生成任何可执行的文件。

从原理上讲,go run 只是将编译后生成的可执行文件放到临时目录中执行,工作目录仍然为当前目录。同时,go run 命令允许添加参数,这些参数将作为 go 程序的可接受参数使用。

由此可见,go run 命令同样会执行编译操作。但要注意的是,go run 不适用于包的执行。

# gofmt

gofmt 命令的作用是将代码按照Go语言官方提供的代码风格进行格式化操作。

请大家注意,gofmt和go fmt是两个不同的命令。go fmt 命令是 gofmt 的封装,go fmt 支持两个参数:-n 和 -x,分别表示仅输出格式化时执行的命令,以及执行格式化并输出格式化时执行的命令。

执行 gofmt 命令时,可指定文件或目录,也可不指定。当不指定时,gofmt 命令会搜索当前目录中的 go 源码文件,并执行相应的格式化操作。

gofmt 命令还允许附加参数,具体参数和作用如下表所示:

参数名 作用
-l 仅输出需要进行代码格式化的源码文件的绝对路径
-w 进行代码格式化,并用改写后的源码覆盖原有源码
-r rule 添加自定义的代码格式化规则(使用rule表示),格式为:pattern -> replacement
-s 开启源码简化
-d 对比输出代码格式化前后的不同,依赖diff命令
-e 输出所有的语法错误,默认只会打印每行第1个错误,且最多打印10个错误
-comments 是否保留代码注释,默认值为true
-tabwidth x 用于指定代码缩进的空格数量(使用x表示),默认值为8,该参数仅在-tabs参数为false时生效
-tabs 用于指定代码缩进是否使用tab(“\t”),默认值为true
-cpuprofile filename 是否开启CPU用量分析,需要给定记录文件(使用filename表示),分析结果将保存在这个文件中
💡 提示:使用-s参数进行源码简化的规则请参考:https://pkg.go.dev/cmd/gofmt#hdr-The_simplify_command

# go install

go install 命令的作用和 go build 类似,都是将源码编译为可执行的文件,附加参数也基本通用,这里就不再赘述了。区别在于:

  • go install 命令在编译源码后,会将可执行文件或库文件安装到约定的目录下;
  • go install 命令生成的可执行文件使用包名来命名;
  • 默认情况下,go install 命令会将可执行文件安装到 GOPATH\bin 目录下,依赖的三方包会被安装到 GOPATH\bin 目录下。

# go get

go get 命令的作用是获取源码包,这一操作包含两个步骤,分别是下载源码和执行 go install 命令进行安装。使用时,仅需将源码仓库地址追加到 go get 后即可(访问pkg.go.dev/ (opens new window),搜索包名,在包详情页可以找到仓库地址),例如:

bash
复制代码go get github.com/ethereum/go-ethereum

go get 命令还允许附加参数,具体参数和作用如下表所示:

参数名 作用
-d 仅下载源码包,不安装
-f 在执行-u参数操作时,不验证导入的每个包的获取状态
-fix 在下载源码包后先执行fix操作
-t 获取运行测试所需要的包
-u 更新源码包到最新版本
-u=patch 只小版本地更新源码包,如从1.1.0到1.1.16
-v 执行获取并显示实时日志
-insecure 允许通过未加密的HTTP方式获取

若要指定所获取源码包的版本,可以通过添加 “@版本号” 的方式执行。如:

bash
复制代码go get github.com/ethereum/go-ethereum@v1.10.1

在使用 Go SDK 1.17 版本时,有一点需要额外注意:执行 go get 命令可能会收到警告,大意是 go get 命令是不建议使用的。此时,使用go install替换 go get 即可,原因是在未来的 Go SDK 版本中 go get 的作用等同于 go get -d。

如果你对 “go get” 命令感兴趣,可以阅读官方对它的说明,写的非常详细:docs.studygolang.com/doc/go-get-… (opens new window)

# 小结

🎉 恭喜,您完成了本次课程的学习!

📌 以下是本次课程的重点内容总结,需要牢牢把握:

  1. 了解 Hello World 每行源码的含义,了解 Go 程序源码的结构;
  2. 掌握为源码添加注释的两种方法;
  3. 熟练使用 Go SDK 提供的命令行工具(6 个 Go SDK 命令),包括每个命令的参数。

➡️ 在下次课程中,我们会阐述如下内容:

  1. Go 语言集成开发环境介绍(使用 GoLand );
  2. 如何使用 GoLand 创建、编译和运行 Go 程序;
  3. 如何使用 GoLand 进行 Go 代码调试。