Skip to content

03|基础类型

Go 是静态类型语言,每个变量在声明时就确定了类型,编译阶段会检查类型是否匹配。这个特性减少了运行时因类型混乱导致的意外,但也意味着写代码时需要知道每种数据应该用什么类型。运维和后端开发里最常用的基础类型有四个:字符串、整数、浮点数和布尔值。理解它们的范围、精度和行为差异,能避免数据溢出、精度丢失和类型不匹配导致的编译错误。

一、string

string 是不可变的字节序列,内部存储 UTF-8 编码的文本:

go
package main

import "fmt"

func main() {
	host := "api.example.com"
	path := "/health"
	fmt.Println(host + path) // api.example.com/health
}

字符串用双引号包裹,支持转义字符。原始字符串用反引号包裹,内部不处理转义,适合写正则表达式或多行文本:

go
pattern := `[a-z]+\.[a-z]+`
config := `
server:
  port: 8080
`

字符串不可变意味着修改字符串会生成新的字符串。频繁拼接字符串时,用 strings.Builder+ 运算符更高效:

go
import "strings"

var b strings.Builder
b.WriteString("host=")
b.WriteString(host)
b.WriteString("&port=")
b.WriteString(strconv.Itoa(port))
result := b.String()

二、整数类型

Go 的整数类型按大小和符号分为多种:

类型大小范围典型用途
int平台相关(32/64位)依赖平台通用整数,默认选择
int88位-128 ~ 127二进制协议、位操作
int1616位-32768 ~ 32767较少使用
int3232位-21亿 ~ 21亿Unicode码点、网络协议
int6464位非常大文件大小、时间戳、ID
uint平台相关非负计数器、索引
uintptr指针大小非负底层编程、C交互

普通运维和后端开发用 int 足够。端口号(1–65535)、计数、索引用 int 可以容纳。需要精确大小时再用 int64int32

int 在 32 位系统上是 32 位,在 64 位系统上是 64 位。需要跨平台一致的大小时,显式用 int64

整数溢出不会 panic,而是静默回绕。uint8 的值加到 256 会变成 0,这在计算校验和时是有意行为,但在普通计数器里可能导致逻辑错误:

go
var count uint8 = 250
count += 10
fmt.Println(count) // 4,不是 260

三、浮点数

Go 的浮点类型有 float32float64。普通开发用 float64,因为 float32 精度有限,累积误差更明显:

类型精度典型用途
float32约7位有效数字图形计算、机器学习权重
float64约15位有效数字CPU/内存使用率、延迟、百分比

float64 不适合精确计算。两个 float64 值直接用 == 比较,可能因精度问题返回不相等:

go
a := 0.1 + 0.2
fmt.Println(a == 0.3) // false

判断使用率是否超过阈值时,用 >< 比较,不判断相等:

go
usage := 78.5
if usage > 90.0 {
	fmt.Println("critical")
}

需要精确计算的场景(如金额),用 int64 存储最小单位(分),避免浮点精度问题。

四、bool

bool 只有 truefalse 两个值。Go 不会把整数或字符串隐式转成布尔值,if 1if "ok" 都会编译报错:

go
var active bool = true
if active {
	fmt.Println("running")
}

布尔值常用于功能开关、检查结果、条件判断。需要注意:零值是 false,如果一个标志忘了初始化,默认就是关闭状态。

五、类型转换

Go 不允许隐式类型转换。intfloat64 之间、不同大小的整数之间,必须显式转换:

go
var port int = 80
var portFloat float64 = float64(port) // 必须显式转换

var total int64 = 1024
var totalInt int = int(total)         // int64 转 int,可能截断

显式转换的代价是可能丢失精度。int64 的值超过 int 能表示的范围时,转换后结果会截断,但编译器不会报错。这种错误在 32 位系统上更容易出现——大文件的字节数用 int 表示时可能溢出。

字符串和数字之间也不能隐式转换。把端口号拼进 URL 时,需要用 strconv.Itoa

go
import "strconv"

port := 8080
url := "http://127.0.0.1:" + strconv.Itoa(port)

反过来,把字符串转成数字用 strconv.Atoi,它返回两个值:转换结果和错误:

go
portStr := "8080"
port, err := strconv.Atoi(portStr)
if err != nil {
	fmt.Println("invalid port:", err)
}

strconv.ParseIntParseFloat 提供更细粒度的控制,可以指定进制和位数:

go
value, err := strconv.ParseInt("FF", 16, 64) // 255
value, err := strconv.ParseFloat("78.5", 64)

六、类型选择建议

场景推荐类型原因
端口号、计数、索引int够大、运算方便
使用率、延迟、百分比float64有小数部分
服务名、路径、日志string文本数据
开关、检查结果bool只有两种状态
需要精确计算(如金额)int64 分单位存储避免浮点精度问题
文件大小、磁盘容量(精确到字节)int64避免超过 2^53 时浮点精度丢失

磁盘容量、文件大小这类数据,如果精确到字节,用 int64float64 更安全。float64 在超过 2^53 时无法精确表示每一个整数,大文件的字节数可能出现偏差。

七、常见错误

类型不匹配导致编译失败

go
var a int = 10
var b int64 = 20
sum := a + b // 编译错误:invalid operation: a + b (mismatched types int and int64)

不同数值类型之间不能直接运算,必须先显式转换其中一个。

字符串与数字混用

go
port := 80
url := "http://127.0.0.1:" + port // 编译错误:cannot convert port to string

必须用 strconv.Itoafmt.Sprintf

go
url := fmt.Sprintf("http://127.0.0.1:%d", port)

忽略 strconv 的错误返回值

go
port, _ := strconv.Atoi(portStr) // 危险:忽略了解析错误

输入不是数字时,port 会变成 0,后续逻辑可能把 0 当成正常值处理。所有 strconv 函数的错误都应该检查。

整数除法截断

go
result := 5 / 2 // 结果是 2,不是 2.5

两个整数相除,结果还是整数,小数部分被截断。需要浮点结果时,先转换:

go
result := float64(5) / float64(2) // 2.5