🟢 基础入门 | Go 入门到精通 2026(三):第一个程序 Hello World
📅 更新于 2026年6月 | ✍️ 原创文章,转载请注明出处 | 作者:布朗克168
开篇
编程界的传统:学任何新语言,第一个程序必须是 Hello World。这不是形式主义------一个简单的 Hello World 背后,藏着语言的程序结构、编译模型和运行机制。
🎯 本文目标:写完 Hello World 后,你能理解每一个关键字和每一行代码在做什么,而不只是"运行成功就行"。
1. 创建你的第一个 Go 程序
步骤 1:创建项目目录
bash
mkdir ~/go-projects/hello
cd ~/go-projects/hello
步骤 2:初始化 Go Module
bash
go mod init hello
步骤 3:创建 main.go
bash
touch main.go
用你喜欢的编辑器打开 main.go,输入以下代码:
go
package main
import "fmt"
func main() {
fmt.Println("Hello, World! 🌍")
fmt.Println("欢迎来到 Go 语言的世界!")
}
步骤 4:运行
bash
$ go run main.go
Hello, World! 🌍
欢迎来到 Go 语言的世界!
🎉 恭喜!你已经是一个 Go 程序员了(至少是初级的)。
2. 完整程序结构逐行分析
让我们把上面的 6 行代码拆开了揉碎了看:
go
package main
| 要素 | 说明 |
|---|---|
package |
Go 的关键字,声明当前文件属于哪个包 |
main |
特殊的包名,告诉 Go 编译器这是一个可执行程序的入口 |
| 位置 | 必须是文件的第一行有效代码(注释除外) |
Go 中每个 .go 文件都必须属于一个包。包名是 main 时,编译器会把它编译成一个可执行文件;包名是其他名字(如 util、models)时,编译成库(.a 文件)。
go
import "fmt"
| 要素 | 说明 |
|---|---|
import |
导入其他包的关键字 |
"fmt" |
Go 标准库中的格式化 I/O 包(类似 C 的 stdio.h) |
fmt 是 Go 最常用的标准库之一,提供了格式化输入输出功能:
fmt.Println()--- 打印并换行fmt.Printf()--- 格式化打印fmt.Scanln()--- 读取输入fmt.Sprintf()--- 格式化返回字符串
如果需要导入多个包,有两种写法:
go
// 写法一:多行 import(推荐)
import (
"fmt"
"os"
"time"
)
// 写法二:多个单行 import(不推荐)
import "fmt"
import "os"
import "time"
go
func main() {
// ...
}
| 要素 | 说明 |
|---|---|
func |
声明函数的关键字 |
main |
特殊函数名,程序的唯一入口 |
() |
参数列表(此处无参数) |
{ |
函数体开始(必须和 func 在同一行) |
⚠️ Go 的"左大括号不能换行"规则:
go// ✅ 正确 func main() { // ❌ 错误!编译不通过 func main() {这是 Go 编译器强制要求的,目的是统一代码风格。
go
fmt.Println("Hello, World! 🌍")
这里做了两件事:
- 调用
fmt包的Println函数(注意:Println首字母大写,表示它是公开的/导出的) - 传入 一个字符串参数
"Hello, World! 🌍"
Go 的可见性规则极其简洁:首字母大写 = 公开(exported),首字母小写 = 私有(unexported) 。没有 public/private/protected 关键字。
3. 三种运行方式:go run vs go build vs go install
Go 提供了三种运行代码的方式,各有适用场景:
3.1 go run ------ 快速运行
bash
go run main.go
- 做了什么:编译 + 运行,但不保留编译产物
- 适用场景:开发调试、快速验证
- 特点:最方便,但每次都要重新编译
bash
# 可以同时运行多个文件
go run main.go helper.go
# 运行整个包
go run .
3.2 go build ------ 编译成二进制
bash
go build main.go
# 生成 main(Linux/Mac)或 main.exe(Windows)
# 指定输出文件名
go build -o hello main.go
# 生成 hello 或 hello.exe
- 做了什么:只编译,不运行,生成可执行文件
- 适用场景:生产部署、发布
- 特点:一次编译,到处运行(同平台)
bash
# 编译整个模块
go build ./...
# 编译并输出到指定目录
go build -o bin/hello .
3.3 go install ------ 编译并安装
bash
go install .
# 编译并将二进制放到 $GOPATH/bin/ 下
- 做了什么 :编译 + 安装到
$GOPATH/bin - 适用场景 :安装 Go 工具(如
gopls、staticcheck) - 特点:编译产物放在统一位置,方便全局调用
三者的对比
| 命令 | 编译 | 运行 | 保留产物 | 产物位置 | 最佳场景 |
|---|---|---|---|---|---|
go run |
✅ | ✅ | ❌ | 临时目录 | 开发调试 |
go build |
✅ | ❌ | ✅ | 当前目录 | 生产部署 |
go install |
✅ | ❌ | ✅ | $GOPATH/bin |
工具安装 |
关于 go build 的更多选项
bash
# 查看编译详情
go build -v .
# 减小二进制体积(去掉调试信息和符号表)
go build -ldflags="-s -w" -o hello .
# 查看二进制大小
ls -lh hello
# -rwxr-xr-x 1 user staff 1.9M hello # 未压缩
# -rwxr-xr-x 1 user staff 1.3M hello # 压缩后
4. 编译产物深度分析
让我们深入看看 Go 编译出来的二进制到底是个什么东西。
查看文件信息
bash
# macOS/Linux
$ file hello
hello: Mach-O 64-bit executable arm64 # Apple Silicon
# 或
hello: ELF 64-bit LSB executable, x86-64 # Linux
# 查看大小
$ ls -lh hello
-rwxr-xr-x 1 user staff 1.9M hello
# 查看依赖的动态库
$ otool -L hello # macOS
$ ldd hello # Linux
# Go 默认静态链接,通常会输出 "not a dynamic executable"
编译过程简析
Go 的编译过程大体如下:
源代码 (.go)
│
▼
词法分析 → Token 流
│
▼
语法分析 → AST(抽象语法树)
│
▼
类型检查
│
▼
中间代码生成(SSA)
│
▼
优化
│
▼
机器码生成
│
▼
链接(静态链接所有依赖)
│
▼
可执行文件
💡 关键差异:Go 默认静态链接,把运行时(runtime)、垃圾回收器、goroutine 调度器一起打包到二进制里。这就是为什么 Go 程序不需要目标机器安装 Go 环境就能运行。
查看编译过程
bash
# 打印编译过程
go build -x .
# 查看生成的汇编代码
go tool compile -S main.go
# 查看编译器优化决策
go build -gcflags="-m" .
# 输出中会看到逃逸分析等信息
5. go fmt:代码格式化
Go 从诞生之初就内置了代码格式化工具,目的很简单:消灭"代码风格之争"。
基本用法
bash
# 格式化单个文件
go fmt main.go
# 格式化整个项目
go fmt ./...
# 查看哪些文件需要格式化(不实际修改)
gofmt -l .
格式化什么?
把你的代码故意写成这样:
go
package main
import "fmt"
func main(){
x:=1+2
fmt.Println( x )
}
运行 go fmt 后:
go
package main
import "fmt"
func main() {
x := 1 + 2
fmt.Println(x)
}
自动修复了:
- 缩进(Tab 缩进)
- 空格(运算符两边加空格)
- 空行(import 上下加空行)
goimports:更智能的格式化
goimports 在 gofmt 的基础上,还自动管理 import 语句:
bash
# 安装
go install golang.org/x/tools/cmd/goimports@latest
# 使用
goimports -w main.go
# 自动添加缺失的 import,移除未使用的 import
🔧 VS Code 中设置
"go.formatTool": "goimports"后,每次保存都会自动格式化 + 整理 import。
6. 常见编译错误及排查
初学 Go 时,你大概率会遇到以下错误。别怕,我都帮你整理好了:
错误 1:package main is not in GOROOT
package hello/main is not in GOROOT (/usr/local/go/src/hello/main)
原因:Go 在 GOPATH 或 GOROOT 中找不到你的包。
解决:
bash
# 确保在项目目录中初始化了 go.mod
cd ~/go-projects/hello
go mod init hello
错误 2:main.go:1:1: expected 'package', found 'import'
go
// ❌ 错误:import 在 package 之前
import "fmt"
package main
解决 :package 声明必须在所有非注释代码的第一行。
go
// ✅ 正确
package main
import "fmt"
错误 3:undefined: Println
go
package main
func main() {
Println("Hello") // ❌ 缺少包名
}
解决:调用其他包的公开函数时必须带上包名前缀:
go
fmt.Println("Hello") // ✅
错误 4:imported and not used: "fmt"
go
package main
import "fmt" // ❌ 导入了但没使用
func main() {
// 什么也没做
}
解决 :Go 不允许导入未使用的包------这在 Go 中是编译错误,不是警告。
bash
# 删除多余的 import,或者用空白标识符占位
import _ "fmt" // 仅执行 init 函数,不直接使用
错误 5:syntax error: unexpected semicolon or newline before {
go
func main() // ❌ 左大括号必须在同一行
{
解决:Go 编译器会在行末自动插入分号,所以左大括号不能换行。
错误 6:main redeclared in this block
go
func main() {} // ❌ 同一包中只能有一个 main 函数
func main() {} // ❌ 重复定义
解决 :一个 main 包里只能有一个 main 函数。
错误速查表
| 错误信息关键词 | 原因 | 解决办法 |
|---|---|---|
expected 'package' |
package 不在第一行 | 把 package 声明移到第一行 |
undefined |
未导入包或未声明 | 检查 import 和变量声明 |
imported and not used |
导入了但没使用 | 删除或加 _ 前缀 |
declared and not used |
变量声明了但没使用 | 删除或使用它 |
cannot use |
类型不匹配 | 检查类型,可能需要显式转换 |
missing return |
有返回值的函数缺少 return | 添加 return 语句 |
7. go doc:查阅文档
Go 自带文档工具,不需要联网就能查看标准库文档。
基本用法
bash
# 查看包的文档
go doc fmt
# 查看特定函数
go doc fmt.Println
# 查看特定类型的方法
go doc net/http.ServeMux
# 在浏览器中查看文档
go doc -http=:6060
# 然后打开 http://localhost:6060
在终端中快速查看
bash
# 查看 fmt.Printf 的格式化动词
go doc fmt.Printf
# 查看 strings 包有哪些函数
go doc strings | head -20
# 搜索文档(Go 1.19+)
go doc -all strings | grep -i "replace"
写好自己的文档
在 Go 中,注释就是文档:
go
// Package calculator 提供基本的数学计算功能。
package calculator
// Add 返回两个整数的和。
//
// 示例:
//
// sum := Add(1, 2)
// fmt.Println(sum) // 3
func Add(a, b int) int {
return a + b
}
📌 规范:文档注释以被注释的标识符名称开头,
// Add ...。
8. 跨平台交叉编译
Go 的一大杀器:一套代码,编译到任何平台。
基本用法
GOOS(目标操作系统)和 GOARCH(目标 CPU 架构)的排列组合:
bash
# 在 Mac 上编译 Linux 程序
GOOS=linux GOARCH=amd64 go build -o hello-linux main.go
# 在 Mac 上编译 Windows 程序
GOOS=windows GOARCH=amd64 go build -o hello.exe main.go
# 在 Linux 上编译 Mac 程序
GOOS=darwin GOARCH=arm64 go build -o hello-mac main.go
# 编译 ARM Linux(树莓派)
GOOS=linux GOARCH=arm64 go build -o hello-arm main.go
支持的平台组合
| GOOS | GOARCH | 说明 |
|---|---|---|
linux |
amd64 |
Linux x86-64 |
linux |
arm64 |
Linux ARM64(树莓派4/5) |
darwin |
amd64 |
macOS Intel |
darwin |
arm64 |
macOS Apple Silicon |
windows |
amd64 |
Windows x86-64 |
windows |
arm64 |
Windows ARM |
linux |
riscv64 |
RISC-V 架构 |
查看完整列表:
bash
go tool dist list
禁用 CGO 的交叉编译
如果你的代码不依赖 C 库,建议禁用 CGO 以获得纯静态编译:
bash
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o hello-linux main.go
交叉编译脚本
把常用的编译命令写进 Makefile 或脚本:
makefile
# Makefile
.PHONY: build-all
build-all:
GOOS=linux GOARCH=amd64 go build -o bin/hello-linux-amd64 .
GOOS=linux GOARCH=arm64 go build -o bin/hello-linux-arm64 .
GOOS=darwin GOARCH=amd64 go build -o bin/hello-darwin-amd64 .
GOOS=darwin GOARCH=arm64 go build -o bin/hello-darwin-arm64 .
GOOS=windows GOARCH=amd64 go build -o bin/hello-windows.exe .
@echo "✅ 所有平台编译完成!"
9. Hello World 变体:多语言版
既然学会了 Hello World,来看看 Go 能玩出什么花样:
并发版 Hello World
go
package main
import (
"fmt"
"time"
)
func main() {
messages := []string{
"Hello, World! 👋",
"Hola, Mundo! 🌮",
"Bonjour le Monde! 🥐",
"Ciao Mondo! 🍕",
"こんにちは世界! 🗾",
}
for _, msg := range messages {
go func(m string) {
fmt.Println(m)
}(msg)
}
// 等待 goroutine 执行完成
time.Sleep(100 * time.Millisecond)
}
HTTP 服务器版 Hello World
go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World! 🌍\n")
fmt.Fprintf(w, "你访问的路径是:%s\n", r.URL.Path)
})
fmt.Println("服务器已启动:http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
// 运行后,打开浏览器访问 http://localhost:8080
仅仅 15 行代码,你就写出了一个 HTTP 服务器!不需要 Tomcat、不需要 Nginx、不需要任何框架。这就是 Go 标准库的强大之处。
小结与互动
📝 本文要点回顾:
- Go 程序从
package main+func main()开始 package声明必须在第一行,import导入依赖- 三种运行方式:
go run(调试)、go build(编译)、go install(安装) - Go 编译为静态链接的单二进制文件,运行不需要 Go 环境
go fmt强制统一代码格式,告别风格之争- Go 不允许未使用的导入和变量------这是编译错误
go doc离线查看文档- 交叉编译一套命令搞定多平台:
GOOS=linux GOARCH=amd64 go build
❓ 互动问题:
- 你的第一个 Hello World 跑起来了吗?
- 你觉得 Go 不允许未使用的变量/导入这个设计是好是坏?
- 试试改一下 Hello World,打印一些中文或其他内容,看看效果如何?
参考资料
🚀 下一篇 :【Go 入门到精通 2026(四):变量与数据类型】,带你掌握 Go 的类型系统,从此告别
var tmp = ???的困惑!
本文由 布朗克168 原创发布,如需转载请联系作者并注明出处。