惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
WordPress大学
WordPress大学
博客园 - 三生石上(FineUI控件)
J
Java Code Geeks
Vercel News
Vercel News
aimingoo的专栏
aimingoo的专栏
T
Tailwind CSS Blog
罗磊的独立博客
B
Blog
博客园_首页
A
About on SuperTechFans
有赞技术团队
有赞技术团队
V
V2EX
U
Unit 42
I
InfoQ
IT之家
IT之家
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Stack Overflow Blog
Stack Overflow Blog
The Cloudflare Blog
H
Help Net Security

Chongxiの咖啡屋

Google相册迁移到iCloud分享 - Chongxiの咖啡屋 讲讲「万物皆虚,万事皆允」 - Chongxiの咖啡屋 舞萌DX 落雪查分器使用指南 - Chongxiの咖啡屋 现代人的刻舟求剑 - Chongxiの咖啡屋 舞萌DX错误码0010深度解析及处理方法 - Chongxiの咖啡屋 做了一个明日方舟-奇象巡展的像素画拼豆工具 - Chongxiの咖啡屋 舞萌DX国服封禁机制详解 - Chongxiの咖啡屋 舞萌开机网络自检底层技术说明,断网如何排查讲解 - Chongxiの咖啡屋 舞萌机台开机初始化进程详解 - Chongxiの咖啡屋 为什么喜欢音乐 反而不该去学音乐? - Chongxiの咖啡屋 从装逼到嘉豪:中国互联网的精神胜利进化史 - Chongxiの咖啡屋 游蒋介石故居:奉化溪口 - Chongxiの咖啡屋 钢铁雄心4 steam正版导入全DLC补丁 - Chongxiの咖啡屋 漫漫长夜全地图及介绍 - Chongxiの咖啡屋 记一次无需实名换绑米哈游通行证手机号 - Chongxiの咖啡屋 邮储银行万事达借记卡全面指南 - Chongxiの咖啡屋 x402:一个来自27年前的状态码能救活被ai榨干的「人类网站」吗 - Chongxiの咖啡屋 舞萌国人口大普查:全服玩家数据分析 - Chongxiの咖啡屋 家中网络「捉奸」记 - Chongxiの咖啡屋 中国移动PT927G光猫折腾指南:破解超级密码改桥接 - Chongxiの咖啡屋 最好用的Android邮件客户端推荐 - Chongxiの咖啡屋 如何验证对方是不是Chongxi我本人 - Chongxiの咖啡屋 三星S20系列刷入移植OneUI8流程 - Chongxiの咖啡屋 使用obisidian和git插件来完美管理静态博客 - Chongxiの咖啡屋 玩客云拆机教程 - Chongxiの咖啡屋 母语级英语的本质,不是英语 - Chongxiの咖啡屋 我用过最好用的Android桌面(启动器) - Chongxiの咖啡屋 1. 区块链基本知识 - Chongxiの咖啡屋 b站创作激励提现几乎无门槛了 - Chongxiの咖啡屋 速报:APKPure 版 Telegram 被植入恶意后门 - Chongxiの咖啡屋
Ch.07:Go的类型转换 - Chongxiの咖啡屋
Chongxi · 2026-05-04 · via Chongxiの咖啡屋

Go 有一条规则你必须得记:不同类型之间不能直接运算或赋值,必须显式转换。 没有隐式转换,没有自动提升,一切都要明确写出来

为什么没有隐式转换#

很多语言会自动转换类型,比如 C 里 intfloat 可以直接运算,语言帮你悄悄处理。Go 不打算这样干

原因很简单:隐式转换会掩盖 bug。你以为两个数字在相加,实际上发生了精度丢失或者溢出,但没有任何提示。Go 让一切都显式发生,出了问题一眼看出来

PRTCL // GO

var a int = 10

var b float64 = 3.14

fmt.Println(a + b) // 报错:mismatched types int and float64

必须手动转换:

PRTCL // GO

fmt.Println(float64(a) + b) // 10 + 3.14 = 13.14

数字类型之间的转换#

语法是 目标类型 ( 值 )

PRTCL // GO

var a int = 42

var b float64 = float64(a) // int → float64

var c int = int(b) // float64 → int

简单吧,但有几个细节要注意

浮点转整数:直接截断,不四舍五入

PRTCL // GO

var f float64 = 3.99

var n int = int(f)

fmt.Println(n) // 3,不是 4

小数部分直接丢掉,不管是 3.1 还是 3.9,结果都是 3。需要四舍五入要用 math.Round()

PRTCL // GO

import "math"

f := 3.99

n := int(math.Round(f))

fmt.Println(n) // 4

大类型转小类型可能会溢出

PRTCL // GO

var a int32 = 1000

var b int8 = int8(a)

fmt.Println(b) // -24,溢出了

int8 最大 127,1000 放不下,发生溢出。Go 不报错,静默地给你错误的结果,这是最危险的情况,你要自己保证转换前值在目标类型的范围内

有符号和无符号互转同样可能出问题

PRTCL // GO

var a int = -1

var b uint = uint(a)

fmt.Println(b) // 18446744073709551615,-1 变成了极大的正数

负数转无符号整数,结果完全不符合预期

整数和浮点互转的精度损失

PRTCL // GO

var a int64 = 9999999999999999

var b float64 = float64(a)

var c int64 = int64(b)

fmt.Println(a == c) // false,精度丢失了

float64 的有效精度约 15-16 位,超大整数转浮点再转回来,可能和原始值不一样

字符串和 []byte、[]rune 互转#

上一篇讲过,这里整理一下:

PRTCL // GO

s := "Hello, 世界"

// string → []byte

bs := []byte(s)

// []byte → string

s2 := string(bs)

// string → []rune

rs := []rune(s)

// []rune → string

s3 := string(rs)

单个数字转字符串,不要用 string()

PRTCL // GO

n := 65

fmt.Println(string(n)) // "A",不是 "65"

string(65) 把 65 当作 Unicode 码点,返回对应的字符 "A",不是数字 65 的字符串形式。要得到 "65"strconv.Itoa()

strconv 包:字符串和数字互转#

字符串和数字之间不能用类型转换语法,要用 strconv

PRTCL // GO

import "strconv"

整数和字符串#

整数转字符串,Itoa

PRTCL // GO

n := 42

s := strconv.Itoa(n)

fmt.Println(s) // "42"

fmt.Printf("%T\n", s) // string

字符串转整数,Atoi

PRTCL // GO

s := "42"

n, err := strconv.Atoi(s)

if err != nil {

fmt.Println("转换失败:", err)

} else {

fmt.Println(n) // 42

}

Atoi 返回两个值:结果和错误。字符串不一定是合法的整数,所以必须处理错误:

PRTCL // GO

s := "abc"

n, err := strconv.Atoi(s)

if err != nil {

fmt.Println(err) // strconv.Atoi: parsing "abc": invalid syntax

}

fmt.Println(n) // 0,转换失败时结果是零值

浮点数和字符串#

浮点数转字符串,FormatFloat

PRTCL // GO

f := 3.14159

// 'f' 格式(普通小数),2 位小数,float64

s := strconv.FormatFloat(f, 'f', 2, 64)

fmt.Println(s) // "3.14"

// 'e' 格式(科学计数法)

s2 := strconv.FormatFloat(f, 'e', 4, 64)

fmt.Println(s2) // "3.1416e+00"

// 'g' 格式(自动选择最短表示)

s3 := strconv.FormatFloat(f, 'g', -1, 64)

fmt.Println(s3) // "3.14159"

参数说明:第一个是值,第二个是格式(f/e/g),第三个是精度(-1 表示最短表示),第四个是位数(32 或 64)

字符串转浮点数,ParseFloat

PRTCL // GO

s := "3.14"

f, err := strconv.ParseFloat(s, 64) // 64 表示 float64

if err != nil {

fmt.Println("转换失败:", err)

} else {

fmt.Println(f) // 3.14

}

布尔值和字符串#

布尔转字符串:

PRTCL // GO

b := true

s := strconv.FormatBool(b)

fmt.Println(s) // "true"

字符串转布尔:

PRTCL // GO

s := "true"

b, err := strconv.ParseBool(s)

if err != nil {

fmt.Println("转换失败:", err)

} else {

fmt.Println(b) // true

}

ParseBool 能识别 "1""t""true""TRUE" 为 true,"0""f""false""FALSE" 为 false,其他值报错

ParseInt 和 ParseUint 的通杀解析#

Atoi 只能解析十进制整数,ParseInt 更灵活:

PRTCL // GO

// 解析十六进制

n, err := strconv.ParseInt("ff", 16, 64) // 基数 16,位数 64

fmt.Println(n) // 255

// 解析二进制

n2, err := strconv.ParseInt("1010", 2, 64)

fmt.Println(n2) // 10

// 基数 0:根据前缀自动判断(0x 十六进制,0b 二进制,0o 八进制)

n3, err := strconv.ParseInt("0xff", 0, 64)

fmt.Println(n3) // 255

整数转指定进制的字符串:

PRTCL // GO

n := 255

fmt.Println(strconv.FormatInt(int64(n), 2)) // "11111111",二进制

fmt.Println(strconv.FormatInt(int64(n), 16)) // "ff",十六进制

fmt.Println(strconv.FormatInt(int64(n), 10)) // "255",十进制

fmt.Sprintf 的格式化转换#

除了 strconv,还可以用 fmt.Sprintf 把任意值转成字符串:

PRTCL // GO

n := 42

f := 3.14

b := true

s1 := fmt.Sprintf("%d", n) // "42"

s2 := fmt.Sprintf("%.1f", f) // "3.1"

s3 := fmt.Sprintf("%v", b) // "true"

s4 := fmt.Sprintf("%v", n) // "42",%v 是通用格式

%v 是万能格式,任何类型都能用,但不如专用格式精确。Sprintfstrconv 灵活,但速度稍慢,需要高性能场合用 strconv

接口类型的转换#

接口类型是 Go 里一个特殊的存在,它可以存任意类型的值。从接口类型取回具体类型,需要用类型断言

现在不需要完全理解接口,只需要知道类型断言的语法,后面接口那篇会深入讲

PRTCL // GO

var i interface{} = "hello"

// 类型断言

s, ok := i.(string)

if ok {

fmt.Println(s) // "hello"

} else {

fmt.Println("不是 string")

}

i.(string) 断言 i 里存的是 string,返回两个值:实际值和是否成功

不带 ok 的写法,断言失败会直接 panic:

PRTCL // GO

s := i.(string) // 如果 i 不是 string,程序崩溃

ok 的写法更安全,断言失败 okfalses 是零值,程序不崩溃

自定义类型之间的转换#

上一篇提到可以用 type 定义新类型:

PRTCL // GO

type Celsius float64 // 摄氏度

type Fahrenheit float64 // 华氏度

虽然两者底层都是 float64,但它们定义上是不同的类型,不能直接运算:

PRTCL // GO

var c Celsius = 100

var f Fahrenheit = Fahrenheit(c) // 必须显式转换

底层类型相同的自定义类型之间可以互转:

PRTCL // GO

type Celsius float64

type MyFloat float64

var c Celsius = 100

var m MyFloat = MyFloat(c) // 底层都是 float64,可以转

但自定义类型和内置类型之间也需要显式转换:

PRTCL // GO

type MyInt int

var a MyInt = 10

var b int = int(a) // MyInt → int

var c MyInt = MyInt(b) // int → MyInt

转换性能考量#

大多数类型转换在编译阶段处理,运行时没有额外开销。但有几种转换会分配内存,需要注意:

string[]byte 互转会复制数据:

PRTCL // GO

s := "hello"

bs := []byte(s) // 复制了一份数据

频繁转换在高性能场合是个负担。Go 1.20 之后可以用 unsafe 包做零拷贝转换,但那是进阶话题,现在不用管

fmt.Sprintfstrconv 慢:

做大量数字转字符串,用 strconv.Itoa,不要用 fmt.Sprintf("%d", n)。差距在基准测试里明显,日常少量转换无所谓

常见坑#

string( 整数 ) 不是数字转字符串

PRTCL // GO

fmt.Println(string(65)) // "A",不是 "65"

fmt.Println(strconv.Itoa(65)) // "65",这才对嘛

浮点转整数不四舍五入

PRTCL // GO

int(3.9) // 3,不是 4

需要四舍五入用 math.Round()

大转小溢出不报错

PRTCL // GO

int8(1000) // -24,静默溢出

转换前自己确认值在目标范围内

负数转无符号整数

PRTCL // GO

uint(-1) // 18446744073709551615

有符号负数转无符号,结果完全不符合预期

Atoi 错误没处理

PRTCL // GO

n, _ := strconv.Atoi("abc")

fmt.Println(n) // 0,这里他悄悄失败了

转换失败时结果是零值,如果直接用这个零值继续运算,bug 很难追踪。你要养成错误必须要处理的习惯


下一篇讲运算符,算术、比较、逻辑、位运算,以及 Go 里一些和其他语言不同的地方