Literal(字面量)
字面量(literal)是在源码中直接写出的值或值的构造形式。Go 提供数值、rune、字符串等基本字面量,也提供数组、切片、映射、结构体和函数的字面量。理解字面量的类型推断与表示范围,可以避免溢出、乱码和初始化歧义。
整数字面量
整数可以使用十进制、二进制、八进制或十六进制表示:
package main
import "fmt"
func main() {
decimal := 42
binary := 0b101010
octal := 0o52
hexadecimal := 0x2A
readable := 1_000_000
fmt.Println(decimal, binary, octal, hexadecimal, readable)
}
这些写法的数值分别是 42、42、42、42 和 1000000。下划线可以提高长数字的可读性,不会改变数值,但必须出现在语法允许的数字分隔位置。
没有显式类型约束的整数常量可以保持任意精度,直到它被赋给变量、作为参数传入或参与需要具体类型的运算。目标类型必须能表示该常量:
const maxByte = 255
var valid uint8 = maxByte
// var overflow uint8 = 256 // 编译错误:uint8 无法表示 256
浮点数与复数字面量
浮点数字面量可以使用小数点或指数形式:
package main
import "fmt"
func main() {
price := 19.95
distance := 1.2e3
hexFloat := 0x1.8p1 // 1.5 × 2¹,即 3
fmt.Println(price, distance, hexFloat)
}
二进制指数形式中的 p 表示以 2 为底的指数。浮点数采用有限精度表示,像 0.1 这样的十进制小数通常不能被二进制浮点数精确表示;金额等需要精确十进制定点语义的数据不应直接依赖普通浮点运算。
虚数字面量以 i 结尾,可用于构造复数:
value := 2 + 3i
这里 value 的实部是 2,虚部是 3。
Rune 字面量
rune 字面量使用单引号,表示一个 Unicode 码点,其默认类型为 rune,而 rune 是 int32 的别名:
package main
import "fmt"
func main() {
letter := 'G'
chinese := '语'
newline := '\n'
unicode := '\u8BED'
fmt.Printf("%c %c %q %c\n", letter, chinese, newline, unicode)
}
单引号中必须表示一个码点或合法转义,不能写多个字符。rune 适合表达码点,不等同于用户感知的完整字符;某些视觉字符可能由多个码点组合而成。
字符串字面量
Go 有解释字符串和原始字符串两种写法。
解释字符串
双引号字符串会处理转义序列:
message := "line 1\nline 2\t\"Go\""
path := "C:\\temp\\app.log"
常见转义包括 \n、\t、\\、\",以及 Unicode 转义 \uXXXX、\UXXXXXXXX。
原始字符串
反引号字符串不会解释反斜杠转义,可以跨越多行:
query := `SELECT id, name
FROM users
WHERE active = true`
windowsPath := `C:\temp\app.log`
原始字符串中不能直接包含反引号。它适合正则表达式、SQL 和多行模板,但换行和缩进会成为字符串内容的一部分。
Go 字符串本质上是只读字节序列,源码中的字符串通常使用 UTF-8。len 返回字节数,不是 rune 数量:
fmt.Println(len("Go语言")) // 8
true、false 与 nil
严格来说,true 和 false 是预声明的布尔常量,nil 是预声明标识符,它们都不是关键字式字面量。日常代码仍直接使用它们:
enabled := true
var pointer *int = nil
nil 只能用于指针、函数、切片、映射、通道和接口等具有 nil 零值的类型,不能赋给整数、布尔值、字符串、数组或结构体。
复合字面量
复合字面量(composite literal)可以直接构造数组、切片、映射和结构体:
package main
import "fmt"
type User struct {
ID int
Name string
}
func main() {
array := [3]int{1, 2, 3}
slice := []string{"Go", "TypeScript"}
scores := map[string]int{"Go": 100, "TypeScript": 90}
user := User{ID: 1, Name: "Terry"}
userPointer := &User{ID: 2, Name: "Lan"}
fmt.Println(array, slice, scores, user, userPointer.Name)
}
结构体字面量在包外使用时,通常应写字段名。命名字段能抵抗字段顺序变化,也让初始化意图更清楚。映射和切片字面量会创建已初始化的值,而只声明未初始化变量得到的是 nil 映射或 nil 切片,两者后续操作能力不同。
数组长度还可以由元素数量推断:
levels := [...]string{"debug", "info", "error"}
函数字面量
函数字面量用于创建匿名函数,也可以捕获外层作用域中的变量:
package main
import "fmt"
func main() {
prefix := "Hello"
format := func(name string) string {
return prefix + ", " + name
}
fmt.Println(format("Go"))
}
捕获的是外层变量本身,而不是声明函数时的一份固定文本副本。闭包逃逸、循环变量和并发访问会带来更进一步的生命周期与同步问题,应结合函数和并发专题理解。
与 TypeScript 的区别
两种语言都支持十六进制、模板式多行文本、对象或集合初始化和匿名函数,但语义并不相同。Go 的单引号表示 rune,不是字符串;反引号原始字符串不执行 ${...} 插值;结构体和数组具有明确静态类型;未加类型的数值常量还具有 Go 特有的延迟定型与可表示性规则。
字符串的 UTF-8、码点和字节边界见 String(字符串);类型推断与可赋值性见 Type(类型)。
小结
- 整数支持多种进制和下划线分隔,无类型数值常量在进入具体类型时必须可表示。
- 浮点数可能存在二进制表示误差,精确金额计算需要选择合适的数据表示。
- rune 字面量表示一个 Unicode 码点,字符串则是只读字节序列。
- 双引号字符串处理转义,反引号原始字符串保留大部分原始文本。
- 复合字面量可以构造数组、切片、映射和结构体,函数字面量可以形成闭包。
true、false和nil是预声明标识符,不是关键字。