Go的println和fmt.Println有什么区别

Go 内置的 println 输出到 stderr,fmt.Println 输出到 stdout。println 不能打印结构体、切片输出地址,还可能被废弃,只适合临时 debug。生产代码必须用 fmt.Println,功能完整、格式化支持好。

刚入门 Go 的人经常会分不清 println 和 fmt.Println,用哪个都差不多嘛。其实差别挺大,一个是临时 debug 用的,一个才是生产级别的。

区别一:输出目标不同

println 输出到标准错误流(stderr)。

fmt.Println 输出到标准输出流(stdout)。

这在脚本里就能看出来:

# 假设 Go 程序用 println 输出"hello"
go run main.go 2>/dev/null  # 看不到"hello"(stderr 被丢弃了)
go run main.go 1>/dev/null  # 能看到"hello"(stdout 被丢弃了)
​

所以如果你在脚本里管道输出给另一个命令,千万别用 println,数据会走 stderr 被吞掉。

区别二:功能完整度

println 功能简陋,它只能接收多个参数然后用空格分隔输出,别想着格式化。

println("hello", 42, 3.14)  // hello 42 3.14
println("hello %s", "world")  // hello %s world(不会格式化)
​

fmt.Println 功能齐全,还有 fmt.Printf 支持格式化字符串。

fmt.Println("hello", 42, 3.14)  // hello 42 3.14
fmt.Printf("hello %s\n", "world")  // hello world(正确格式化)
fmt.Sprintf("hello %s", "world")  // 返回格式化后的字符串
​

区别三:复杂数据类型的输出

println 对结构体和切片的输出很"朴素",基本看不出啥东西。

type Person struct {
    Name string
    Age  int
}

p := Person{"Alice", 30}
println(p)  // {Alice 30}

s := []int{1, 2, 3}
println(s)  // [1 2 3](实际上是个地址)
​

fmt.Println 稍好点,还有更专业的 fmt.Sprintf 配合 %v / %+v / %#v 来做深度对象检查。

p := Person{"Alice", 30}
fmt.Println(p)  // {Alice 30}
fmt.Printf("%+v\n", p)  // {Name:Alice Age:30}(带字段名)
fmt.Printf("%#v\n", p)  // main.Person{Name:"Alice", Age:30}(Go 代码形式)

s := []int{1, 2, 3}
fmt.Println(s)  // [1 2 3]
fmt.Printf("%v\n", s)  // [1 2 3]
fmt.Printf("%#v\n", s)  // []int{1, 2, 3}
​

真要深入调试对象,还可以用第三方库 litter 或 spew。

区别四:性能和地位

println 是内置函数,不需要导包,但文档明确说了"for debugging purposes"(用来 debug 的)。

fmt.Println 需要 import fmt 包,是官方推荐的标准方式,功能完整、文档齐全。

从 Go 官方的态度来看,println 就是个"方便调试的玩意儿",随时可能被调整甚至废弃。生产代码千万别赖上它。

什么时候用 println

只有一个场景:快速临时 debug。

func processData(data []int) {
    println("data:", data)  // 快速看一眼
    // ...
}
​

func main() {
x := 42
println("x is:", x) // 快速看一眼
}


而且要记得后来删掉,不能留在代码里提交。

# 什么时候用 fmt.Println

**一切生产代码都应该用 fmt.Println**。

```go
// 日志输出
fmt.Println("[INFO] 程序启动")

// 用户交互
fmt.Println("请输入你的名字:")

// 格式化输出
fmt.Printf("用户 %s,年龄 %d\n", name, age)
​

要定制日志格式的话,通常会用 log 包或者第三方库(logrus、zap 等)。

import "log"

log.Println("[INFO] 程序启动")
log.Printf("用户 %s 登录\n", username)
​

log 包内置了时间戳、日志级别等功能,比 fmt 更适合日志。

完整对比表

特性 println fmt.Println
输出目标 stderr stdout
需要导包 否 是(import fmt)
格式化 不支持 支持(Printf)
结构体输出 简陋 较好(%v 等)
文档地位 Debug 用 标准输出
生产推荐度 ❌ 不推荐 ✅ 强烈推荐
性能 稍快(内置) 稍慢(包函数)

小结

println 是 Go 给你的一个"快速 debug 便利函数",输出到 stderr,功能简单,只适合临时调试。

fmt.Println 是生产级别的标准输出,输出到 stdout,功能完整,支持格式化,是正经代码的标准写法。

简单判断:看到 println 在代码里,说明这是有人临时加上去 debug 的,应该删掉;看到 fmt.Println,才是真正的业务逻辑输出。

更多推荐

章节目录