Appearance
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位) | 依赖平台 | 通用整数,默认选择 |
int8 | 8位 | -128 ~ 127 | 二进制协议、位操作 |
int16 | 16位 | -32768 ~ 32767 | 较少使用 |
int32 | 32位 | -21亿 ~ 21亿 | Unicode码点、网络协议 |
int64 | 64位 | 非常大 | 文件大小、时间戳、ID |
uint | 平台相关 | 非负 | 计数器、索引 |
uintptr | 指针大小 | 非负 | 底层编程、C交互 |
普通运维和后端开发用 int 足够。端口号(1–65535)、计数、索引用 int 可以容纳。需要精确大小时再用 int64 或 int32。
int 在 32 位系统上是 32 位,在 64 位系统上是 64 位。需要跨平台一致的大小时,显式用 int64。
整数溢出不会 panic,而是静默回绕。uint8 的值加到 256 会变成 0,这在计算校验和时是有意行为,但在普通计数器里可能导致逻辑错误:
go
var count uint8 = 250
count += 10
fmt.Println(count) // 4,不是 260三、浮点数
Go 的浮点类型有 float32 和 float64。普通开发用 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 只有 true 和 false 两个值。Go 不会把整数或字符串隐式转成布尔值,if 1 或 if "ok" 都会编译报错:
go
var active bool = true
if active {
fmt.Println("running")
}布尔值常用于功能开关、检查结果、条件判断。需要注意:零值是 false,如果一个标志忘了初始化,默认就是关闭状态。
五、类型转换
Go 不允许隐式类型转换。int 和 float64 之间、不同大小的整数之间,必须显式转换:
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.ParseInt 和 ParseFloat 提供更细粒度的控制,可以指定进制和位数:
go
value, err := strconv.ParseInt("FF", 16, 64) // 255
value, err := strconv.ParseFloat("78.5", 64)六、类型选择建议
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 端口号、计数、索引 | int | 够大、运算方便 |
| 使用率、延迟、百分比 | float64 | 有小数部分 |
| 服务名、路径、日志 | string | 文本数据 |
| 开关、检查结果 | bool | 只有两种状态 |
| 需要精确计算(如金额) | int64 分单位存储 | 避免浮点精度问题 |
| 文件大小、磁盘容量(精确到字节) | int64 | 避免超过 2^53 时浮点精度丢失 |
磁盘容量、文件大小这类数据,如果精确到字节,用 int64 比 float64 更安全。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.Itoa 或 fmt.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