我对这个问题其实也是一直有疑问的,毕竟像 byte 都是 uint8 的别名。然后找了一些问答资料,不知道还没有没其他更好的解释。
范围足够
在 Unicode 字符集中,一个字符的码点范围是从 U+0000 到 U+10FFFF,共计 1114112 个码点,可以用 32 位的整数类型来表示。 Unicode 转换格式,虽然使用的是 4 字节(32bit),但实际上只用到了 21bit。所以 int32 是足够包含该编码范围的。
扩展兼容
使用 int32
类型能有效避免符号扩展等不必要的类型转换,以及提供向下兼容的能力。
算术运算方便
定义为 int32 是为了方便 rune 类型进行算数运算。
package main
import "fmt"
func main() {
s := "hello world"
a := []rune(s)
fmt.Println(a)
b := a[1] - a[0]
if b < 0 {
fmt.Println(b)
}
}
比如像 byte 进行算术运算时,就会出现溢出的情况(所以也建议大家尽量不要拿 byte 的运算结果做条件判断等)。
package main
import "fmt"
func main() {
s := "hello world"
a := []byte(s)
fmt.Println(a) // [104 101 108 108 111 32 119 111 114 108 100]
if a[1] - a[0] > 0 {
fmt.Println("a[1] > a[0]", a[1] - a[0]) // a[1] > a[0] 253
}
if a[0] - a[1] > 0 {
fmt.Println("a[0] > a[1]", a[0] - a[1]) // a[0] > a[1] 3
}
}
uint8 别名 byte
既然如此,还有一个疑问,为什么 Go 中 byte 是 uint8 的别名,而不是 int8 的别名呢?
哈哈,这个就不得不提到 UTF-8 编码了,正好也是 Go 默认的编码方式。
UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用 1~4 个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8 的编码规则很简单,只有二条:
- 对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
- 对于 n 字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
package main
import (
"fmt"
)
func main() {
s := "好"
b := []byte(s)
fmt.Println(b) // [229 165 189]
for _, c := range b {
fmt.Printf("%b ", c) // 11100101 10100101 10111101
}
}
这样的话,在 UTF-8 编码下使用 byte 表示非 ASCII 字符的话(需要多字节表示),就不得不涉及到字节高位的情况了,而字节高位的表示显然是用 int8 无法处理的。
文章评论