欢迎光临
我们一直在努力

Go 入门到精通-03-第一个程序Hello World

🟢 基础入门 | Go 入门到精通 2026(三):第一个程序 Hello World

📅 更新于 2026年6月 | ✍️ 原创文章,转载请注明出处 | 作者:布朗克168



开篇

编程界的传统:学任何新语言,第一个程序必须是 Hello World。这不是形式主义——一个简单的 Hello World 背后,藏着语言的程序结构、编译模型和运行机制。

🎯 本文目标:写完 Hello World 后,你能理解每一个关键字和每一行代码在做什么,而不只是"运行成功就行"。


1. 创建你的第一个 Go 程序

步骤 1:创建项目目录

mkdir ~/go-projects/hello
cd ~/go-projects/hello

步骤 2:初始化 Go Module

go mod init hello

步骤 3:创建 main.go

touch main.go

用你喜欢的编辑器打开 main.go,输入以下代码:

package main

import "fmt"

func main() {
fmt.Println("Hello, World! 🌍")
fmt.Println("欢迎来到 Go 语言的世界!")
}

步骤 4:运行

$ go run main.go
Hello, World! 🌍
欢迎来到 Go 语言的世界!

🎉 恭喜!你已经是一个 Go 程序员了(至少是初级的)。


2. 完整程序结构逐行分析

让我们把上面的 6 行代码拆开了揉碎了看:

package main

要素说明
package Go 的关键字,声明当前文件属于哪个包
main 特殊的包名,告诉 Go 编译器这是一个可执行程序的入口
位置 必须是文件的第一行有效代码(注释除外)

Go 中每个 .go 文件都必须属于一个包。包名是 main 时,编译器会把它编译成一个可执行文件;包名是其他名字(如 util、models)时,编译成库(.a 文件)。

import "fmt"

要素说明
import 导入其他包的关键字
"fmt" Go 标准库中的格式化 I/O 包(类似 C 的 stdio.h)

fmt 是 Go 最常用的标准库之一,提供了格式化输入输出功能:

  • fmt.Println() — 打印并换行
  • fmt.Printf() — 格式化打印
  • fmt.Scanln() — 读取输入
  • fmt.Sprintf() — 格式化返回字符串

如果需要导入多个包,有两种写法:

// 写法一:多行 import(推荐)
import (
"fmt"
"os"
"time"
)

// 写法二:多个单行 import(不推荐)
import "fmt"
import "os"
import "time"

func main() {
// …
}

要素说明
func 声明函数的关键字
main 特殊函数名,程序的唯一入口
() 参数列表(此处无参数)
{ 函数体开始(必须和 func 在同一行)

⚠️ Go 的"左大括号不能换行"规则:

// ✅ 正确
func main() {

// ❌ 错误!编译不通过
func main()
{

这是 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 —— 快速运行

    go run main.go

    • 做了什么:编译 + 运行,但不保留编译产物
    • 适用场景:开发调试、快速验证
    • 特点:最方便,但每次都要重新编译

    # 可以同时运行多个文件
    go run main.go helper.go

    # 运行整个包
    go run .

    3.2 go build —— 编译成二进制

    go build main.go
    # 生成 main(Linux/Mac)或 main.exe(Windows)

    # 指定输出文件名
    go build -o hello main.go
    # 生成 hello 或 hello.exe

    • 做了什么:只编译,不运行,生成可执行文件
    • 适用场景:生产部署、发布
    • 特点:一次编译,到处运行(同平台)

    # 编译整个模块
    go build ./...

    # 编译并输出到指定目录
    go build -o bin/hello .

    3.3 go install —— 编译并安装

    go install .
    # 编译并将二进制放到 $GOPATH/bin/ 下

    • 做了什么:编译 + 安装到 $GOPATH/bin
    • 适用场景:安装 Go 工具(如 gopls、staticcheck)
    • 特点:编译产物放在统一位置,方便全局调用

    三者的对比

    命令编译运行保留产物产物位置最佳场景
    go run 临时目录 开发调试
    go build 当前目录 生产部署
    go install $GOPATH/bin 工具安装

    关于 go build 的更多选项

    # 查看编译详情
    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 编译出来的二进制到底是个什么东西。

    查看文件信息

    # 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 环境就能运行。

    查看编译过程

    # 打印编译过程
    go build -x .

    # 查看生成的汇编代码
    go tool compile -S main.go

    # 查看编译器优化决策
    go build -gcflags="-m" .
    # 输出中会看到逃逸分析等信息


    5. go fmt:代码格式化

    Go 从诞生之初就内置了代码格式化工具,目的很简单:消灭"代码风格之争"。

    基本用法

    # 格式化单个文件
    go fmt main.go

    # 格式化整个项目
    go fmt ./...

    # 查看哪些文件需要格式化(不实际修改)
    gofmt -l .

    格式化什么?

    把你的代码故意写成这样:

    package main
    import "fmt"
    func main(){
    x:=1+2
    fmt.Println( x )
    }

    运行 go fmt 后:

    package main

    import "fmt"

    func main() {
    x := 1 + 2
    fmt.Println(x)
    }

    自动修复了:

    • 缩进(Tab 缩进)
    • 空格(运算符两边加空格)
    • 空行(import 上下加空行)

    goimports:更智能的格式化

    goimports 在 gofmt 的基础上,还自动管理 import 语句:

    # 安装
    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 中找不到你的包。

    解决:

    # 确保在项目目录中初始化了 go.mod
    cd ~/go-projects/hello
    go mod init hello

    错误 2:main.go:1:1: expected 'package', found 'import'

    // ❌ 错误:import 在 package 之前
    import "fmt"
    package main

    解决:package 声明必须在所有非注释代码的第一行。

    // ✅ 正确
    package main
    import "fmt"

    错误 3:undefined: Println

    package main

    func main() {
    Println("Hello") // ❌ 缺少包名
    }

    解决:调用其他包的公开函数时必须带上包名前缀:

    fmt.Println("Hello") // ✅

    错误 4:imported and not used: "fmt"

    package main

    import "fmt" // ❌ 导入了但没使用

    func main() {
    // 什么也没做
    }

    解决:Go 不允许导入未使用的包——这在 Go 中是编译错误,不是警告。

    # 删除多余的 import,或者用空白标识符占位
    import _ "fmt" // 仅执行 init 函数,不直接使用

    错误 5:syntax error: unexpected semicolon or newline before {

    func main() // ❌ 左大括号必须在同一行
    {

    解决:Go 编译器会在行末自动插入分号,所以左大括号不能换行。

    错误 6:main redeclared in this block

    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 自带文档工具,不需要联网就能查看标准库文档。

    基本用法

    # 查看包的文档
    go doc fmt

    # 查看特定函数
    go doc fmt.Println

    # 查看特定类型的方法
    go doc net/http.ServeMux

    # 在浏览器中查看文档
    go doc -http=:6060
    # 然后打开 http://localhost:6060

    在终端中快速查看

    # 查看 fmt.Printf 的格式化动词
    go doc fmt.Printf

    # 查看 strings 包有哪些函数
    go doc strings | head -20

    # 搜索文档(Go 1.19+)
    go doc -all strings | grep -i "replace"

    写好自己的文档

    在 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 架构)的排列组合:

    # 在 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

    支持的平台组合

    GOOSGOARCH说明
    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 架构

    查看完整列表:

    go tool dist list

    禁用 CGO 的交叉编译

    如果你的代码不依赖 C 库,建议禁用 CGO 以获得纯静态编译:

    CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o hello-linux main.go

    交叉编译脚本

    把常用的编译命令写进 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

    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

    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 官方入门教程
    • Go 命令行文档
    • Effective Go
    • Go Build 模式

    🚀 下一篇:[【Go 入门到精通 2026(四):变量与数据类型】],带你掌握 Go 的类型系统,从此告别 var tmp = ??? 的困惑!


    本文由 布朗克168 原创发布,如需转载请联系作者并注明出处。

    赞(0)
    未经允许不得转载:171主机测评 » Go 入门到精通-03-第一个程序Hello World
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址