Go语言类型转换:这些细节不注意,代码可能崩!
在Go语言开发中,类型转换是一个看似简单却暗藏“杀机”的操作。你是否曾因为类型不匹配导致程序崩溃?是否因为浮点数转整数时的截断行为而头疼?甚至是否听说过因类型转换错误导致火箭爆炸的真实案例?本文将带你深入Go语言类型转换的细节,揭示那些容易被忽略的“坑”,帮助你写出更健壮、更可靠的代码。无论你是初学者还是资深开发者,这些细节都可能决定你的代码是“稳如泰山”还是“瞬间崩溃”!
本文深入探讨Go语言中的类型转换,涵盖字符串、数值、布尔类型之间的转换规则及常见问题。通过实际代码示例,详细解析以下内容: - 字符串与数值的转换:如何正确拼接字符串和数值,避免类型不匹配的错误。 - 浮点数与整数的转换:小数点截断与数值溢出的风险。 - 布尔类型的转换:为什么Go语言不支持直接将布尔值转为整数? - 真实案例:1996年阿丽亚娜5号火箭爆炸事件,竟是因为类型转换错误! - 实用技巧:如何使用strconv包和fmt.Sprintf进行安全的类型转换。
文章还提供了小测试和作业题,帮助读者巩固所学知识,避免在实际开发中踩坑。
类型间转换
类型不能混着用
- 连接两个字符串,使用+运算符:
countdown := "Launch in T minus " + "10 seconds."
- 如果想连接字符串和数值,是会报错的:
countdown := "Launch in T minus " + 10 + " seconds."
报错- 整型和浮点类型也不能混着用:
age := 41
marsDays := 687
earthDays := 365.2425
fmt.Println("I am", age*earthDays/marsDays, "years old on Mars.") // 报错
小测试
- Go语言里,“10” – 1这个表达式会报错吗?
数值类型间的转换
- 下例中,如果想把age转化成 浮点类型,需要使用目标类型将其“包裹”起来:
age := 41
marsAge := float64(age)
从浮点类型转为整数类型
- 可以从浮点类型转化为整数类型,小数点后边的部分会被截断,而不是舍入:
earthDays := 365.2425
fmt.Println(int(earthDays))
- 无符号和有符号整数类型之间也需要转换
- 不同大小的整数类型之间也需要转换
类型转换时需要谨慎
1996年,无人驾驶的阿丽亚娜5号火箭偏离飞行路线,解体,发射后仅40秒爆炸。报告的原因是从float64到int16的类型转换错误,其值超过了int16所能容纳的最大值32767。未处理的故障导致飞行控制系统没有方位数据,导致其偏离航向,解体,最终自毁。
类型转换时需谨慎
- “环绕行为”:
var bh float64 = 32768
var h = int16(bh)
fmt.Println(h) // -32768
- 可以通过math包提供的max、min常量,来判断是否超过最大最小值
var bh float64 = 32768
if bh < math.MinInt16 || bh > math.MaxInt16 {
// handle out of range value
}
小测试
- 下列代码输出是什么?
v := 42
if v >= 0 && v <= math.MaxUint8 {
v8 := uint8(v)
fmt.Println("converted:", v8)
}
字符串转换
- 想把rune、byte转化为string,语法是一样的:‘
var pi rune = 960
var alpha rune = 940
var omega rune = 969
var bang byte = 33
fmt.Println(string(pi), string(alpha), string(omega), string(bang))
字符串转换
- 想把数值转化为string,它的值必须能转化为code point。
- strconv包的Itoa函数就可以做这件事:
- Itoa是Integer to ASCII的意思。
- Unicode是ASCII的超集,它们前128个code points是一样的(数字、英文字母、常用标点)。
countdown := 10
str := "Launch in T minus " + strconv.Itoa(countdown) + " seconds."
fmt.Println(str)
- 另外一种把数值转化为string的方式是使用Sprintf函数,和Printf略类似,但是会返回一个string:
countdown := 9
str := fmt.Sprintf("Launch in T minus %v seconds.", countdown)
fmt.Println(str)
-
strconv包里面还有个Atoi(ASCII to Integer)函数。
-
由于字符串里面可能包含任意字符,或者要转换的数字字符串太大,所以Atoi函数可能会发生错误:
- 如果err的值为nil,那么就代表没发生错误。
countdown, err := strconv.Atoi("10")
if err != nil {
// oh no, something went wrong
fmt.Println(err.Error())
}
fmt.Println(countdown)
小测试
- 说出两个函数,它们可以把整数类型转化为string
提示
- Go是静态类型语言,一旦某个变量被声明,那么它的类型就无法再改变了。
布尔类型的转换
- Print家族的函数,会把bool类型的值打印成true/false的文本
launch := false
launchText := fmt.Sprintf("%v", launch)
fmt.Println("Ready for launch:", launchText)
var yesNo string
if launch {
yesNo = "yes"
} else {
yesNo = "no"
}
fmt.Println("Ready for launch:", yesNo)
- 注意:如果你想使用string(false), int(false);bool(1), bool("yes")等类似的方式进行转换,那么Go编译器会报错
- 某些语言里,经常把1和0当作true和false,但是在Go里面不行。
小测试
- 写一个程序,把布尔类型转化为整数类型,true -> 1,false -> 0
作业题
- 写一个程序,把字符串转化为布尔类型:
- “true”, “yes”, “1”是true
- “false”, “no”, “0”是false
- 针对其它值,显示错误信息
测试题
Vigenère cipher维吉尼亚加密算法
- 维吉尼亚加密算法是凯撒加密算法的变种。
- 凯撒算法:
- 之前讲的凯撒算法例子是把字符串中的每个字母都向后移动了3位。
-
假设A=0,B=1 … Z=25。那么,我们就用D=3来表示向后移动3位。
-
维吉尼亚加密算法使用的是密钥(keyword)
-
假设密钥是:GOLANG
第1题
- 编写一个程序来解密下面第一行的文字:
- 它的密钥就是GOLANG
- 所有字母都是大写的英文字母。
第2题
- 编写一个程序:
- 使用维吉尼亚加密算法来加密信息。
- 密钥为GOLANG
- 可能用到的函数:
- strings.Replace
- strings.ToUpper
总结
类型转换是Go语言中一个看似简单却充满陷阱的操作。本文通过详细的代码示例和真实案例,揭示了类型转换中的常见问题及其解决方案。以下是关键要点: 1. 类型不匹配是常见错误:Go语言是静态类型语言,类型转换必须显式进行,混用类型会导致编译错误。 2. 数值转换需谨慎:浮点数转整数时会截断小数部分,而数值溢出可能导致不可预料的后果(如“环绕行为”)。 3. 字符串转换灵活但需注意细节:使用strconv.Itoa和fmt.Sprintf可以安全地将数值转为字符串,但需处理可能的错误。 4. 布尔类型转换受限:Go语言不支持直接将布尔值转为整数,需通过条件语句实现。
通过掌握这些细节,你可以避免因类型转换不当导致的程序崩溃,写出更健壮、更可靠的代码。记住,细节决定成败,尤其是在类型转换这种看似简单的操作中!
小提示
- 如果你在开发中遇到过类型转换的“坑”,欢迎在评论区分享你的经历!
- 完成文章中的小测试和作业题,巩固你的知识,避免未来踩坑。