












在此之前我们装好了环境,跑通了 Hello。但你有没有想过,你写的那几行魔法,计算机怎么跑起来的
在真正开始写代码之前,先花一篇的时间搞清楚这件事,不需要太深入底层,但理解这个过程,会让你少点疑问
计算机本质上是一堆电路,电路只有两种状态:通电和断电。人类用 1 表示通电,0 表示断电,这就是二进制
所有的程序,最终都要变成一串 0 和 1,CPU 才能执行。但没有人会直接写 0 和 1,太痛苦了。所以人类发明了编程语言,用接近自然语言的方式写代码,再让工具把它翻译成机器能懂的东西
这个翻译的过程,有两种主流方式
先把整个源代码翻译成机器码,生成一个可执行文件,然后再运行。翻译这个动作叫编译,做这件事的工具叫编译器
Go 是编译型语言。你写好代码,go build 一下,生成一个二进制文件,直接运行,不需要任何额外环境
PRTCL // PLAINTEXT
源代码 → 编译器 → 可执行文件 → 运行
优点是运行快,因为机器码直接在 CPU 上跑。缺点是每次改完代码都要重新编译
解释型语言
不提前翻译,运行的时候边读代码边执行。做这件事的工具叫解释器
Python 是解释型语言。你写好代码,python hello.py,解释器一行一行读,一行一行执行
PRTCL // PLAINTEXT
源代码 → 解释器 → 运行(边翻译边执行)
优点是改完代码立刻能跑,开发方便。缺点是运行速度比编译型慢,因为翻译是实时发生的
Go 是编译型,但提供了 go run 让你感觉像解释型:它在后台帮你编译,编译完立刻运行,临时文件放在系统缓存目录里,你不用关心中间过程
学习阶段用 go run,部署的时候用 go build,这个习惯记住就行
回头看上一篇那个:
PRTCL // GO
package main
import "fmt"
func main() {
fmt.Println("Hello, Go!")
}
每个文件都属于一个包
Go 的代码是用包(package) 来组织的。每个 .go 文件的第一行,必须声明自己属于哪个包
main 是一个特殊的包名,意思是”这是程序的入口”。Go 运行的时候,会找 package main 里的 main 函数,从那里开始执行
如果你把 package main 改成 package hello,这个文件就变成了一个库,不能直接运行。包的组织方式后面会专门讲,现在只需要记住:能直接运行的程序,包名一定是 main
引入别人写好的代码
import 的意思是”引入这个包,我要用”。fmt 是 Go 标准库里的一个包,提供格式化输入输出的功能
引入多个包时用括号括起来:
PRTCL // GO
import (
"fmt"
"time"
)
Go 有一个强规定:引入了包必须用,不然报错
PRTCL // GO
import (
"fmt"
"time"
)
func main() {
fmt.Println("hello")
// time 没用
}
// 报错:time imported and not used
这个设计和变量必须被使用一样,目的是保持代码干净,不留垃圾
如果有时候你确实需要引入一个包但不直接调用它(比如只需要它的初始化副作用),这时候用 _ 匿名引入:
PRTCL // GO
import _ "some/package"
还可以给包起别名,在包名太长或者有冲突的时候用:
PRTCL // GO
import (
f "fmt"
)
func main() {
f.Println("hello") // 用别名 f 代替 fmt
}
程序从这里开始
PRTCL // GO
func main() {
// 代码写在这里
}
func 是定义函数的关键字,main 是函数名,() 是参数列表,现在是空的,{} 里面是函数体
Go 程序启动时,自动从 func main() 的第一行开始,从上到下依次执行,执行完程序就结束了
遇到函数调用时,会跳进那个函数执行完,再回到原来的位置继续往下走:
PRTCL // GO
func main() {
fmt.Println("第一行") // 1. 先执行这里
fmt.Println("第二行") // 2. 再执行这里
fmt.Println("第三行") // 3. 最后这里
}
输出永远是按顺序的:
PRTCL // PLAINTEXT
第一行
第二行
第三行
这个顺序执行的概念听起来理所当然,但你要真正记住它,因为后面学并发的时候,这个顺序会被打破
PRTCL // GO
fmt.Println("Hello, Go!")
fmt 是包名,Println 是这个包里的一个函数,. 是”属于”的意思
注意 Println 的首字母是大写的。在 Go 里,大小写决定了一个东西能不能被外部访问
fmt.Println 之所以能被我们调用,就是因为它首字母大写,是公开的。如果 Go 官方把它写成 fmt.println,你就调用不了
这个规则贯穿整个 Go,函数、变量、结构体,都遵守这个规定,后面会反复遇到
你可能注意到,Go 的开括号 { 必须和函数声明在同一行:
PRTCL // GO
// 正确
func main() {
fmt.Println("hello")
}
// 报错
func main()
{
fmt.Println("hello")
}
Go 在编译之前,会自动在某些行末尾插入分号。规则很简单:如果一行最后一个词是标识符、数字、字符串,或者 )、]、} 这类符号,就自动加分号
所以 func main() 这一行,因为最后是 ),编译器会自动加分号变成 func main();,然后下一行的 { 就成了孤立的符号,语法错误
把 { 放在同一行就不会触发这个规则
注释是写给人看的,编译器会完全忽略它
单行注释:
PRTCL // GO
// 这是注释
fmt.Println("hello") // 行尾注释
多行注释:
实际项目里单行注释用得更多。多行注释一般用来临时屏蔽一段代码,调试的时候很方便
现在你写的程序只有一个文件,但真实项目不可能把所有代码塞进一个文件里
Go 允许同一个包的代码分散在多个文件里:
PRTCL // PLAINTEXT
golearn/
main.go
helper.go
main.go:
PRTCL // GO
package main
import "fmt"
func main() {
fmt.Println(greet("Chongxi"))
}
helper.go:
PRTCL // GO
package main
func greet(name string) string {
return "你好," + name
}
两个文件都声明了 package main,Go 会把它们当成同一个包处理。运行的时候:
PRTCL // BASH
go run main.go helper.go
# 或者
go run . # 运行当前目录所有 .go 文件
函数和变量的概念后面会讲,这里只是让你知道多文件是怎么回事,不用深究
Go 自带一套工具,开发过程中会经常用到
go run
编译并立刻运行,适合开发阶段:
PRTCL // BASH
go run hello.go
go build
编译生成可执行文件,适合生产部署:
PRTCL // BASH
go build hello.go
./hello
go fmt
自动格式化代码,解决所有缩进和换行问题:
PRTCL // BASH
go fmt hello.go # 格式化单个文件
go fmt ./... # 格式化当前目录所有文件
养成习惯,写完代码跑一下 go fmt,不要手动纠结格式
go vet
静态检查,帮你找代码里的潜在问题,比如格式化字符串用错了类型:
PRTCL // BASH
go vet hello.go
go vet 不报错不代表代码一定对,但它报错了就一定有问题,要认真看
go doc
查看任何包或函数的文档,不用开浏览器:
PRTCL // BASH
go doc fmt
go doc fmt.Println
Go 的标准库文档在 pkg.go.dev/std,所有官方包都在这里,每个函数都有说明和示例
比如你想知道 fmt 里还有哪些函数,打开 pkg.go.dev/fmt 就能看到
遇到不认识的函数或者不确定怎么用,先查这里。标准库文档是 Go 最权威的参考资料
本篇偏概念,现在来过一遍:
package,能直接运行的程序包名是 mainimport 引入包,引入了必须用或者_匿名,可以起别名func main() 开始,从上到下顺序执行go fmt 格式化,go vet 静态检查,go doc 查文档下一篇会讲真正有意思的部分:边亮变量
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。