我对这个问题其实也是一直有疑问的,毕竟像 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
- }
- }
既然如此,还有一个疑问,为什么 Go 中 byte 是 uint8 的别名,而不是 int8 的别名呢?
哈哈,这个就不得不提到 UTF-8 编码了,正好也是 Go 默认的编码方式。
UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用 1~4 个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8 的编码规则很简单,只有二条:
- 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 无法处理的。