Go中int和uint互转的坑
研究varint和zigzag编码的时候,发现一个有趣的问题
例如,我想把一个负数的编码值打印出来
package main
import (
"encoding/binary"
"fmt"
)
func main() {
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, uint32(int32(-10)))
fmt.Printf("%x\n", buf)
}
这段代码编译的时候会报错
.\main.go:10:41: constant -10 overflows uint32
问题出在这里:uint32(int32(-10))
很奇怪,我之前记得无符号和有符号可以互转的啊
再看下面的代码
package main
import (
"bytes"
"encoding/binary"
"fmt"
)
func main() {
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, int32(-10))
fmt.Printf("%x\n", buf.Bytes())
这个代码运行就没问题,来看binary.Write
的实现
先是对输入的数据进行类型判断,然后根据类型写入数据
func Write(w io.Writer, order ByteOrder, data any) error {
// Fast path for basic types and slices.
if n := intDataSize(data); n != 0 {
bs := make([]byte, n)
switch v := data.(type) {
...
case int32:
order.PutUint32(bs, uint32(v))
可以看到,int32在这里显式转换成了uint32并且没有错误
但是这个转换是借助了中间变量,于是我抱着怀疑的态度试了一下
package main
import (
"encoding/binary"
"fmt"
)
func main() {
buf := make([]byte, 4)
foo := int32(-10)
binary.BigEndian.PutUint32(buf, uint32(foo))
fmt.Printf("%x\n", buf)
}
$ go run main.go
fffffff6
倒是没问题,但是这就令人搞不懂了
查了一堆资料之后,找到一个比较合理的解释
uint32(int32(-10))
这个语句是一个常量表达式,在编译时要确定值,如果常量的值不能在目标类型范围内表示就会出错,-10不是无符号数,自然不在int32的范围内,但是使用中间变量之后,会在运行时进行类型转换,这时候可以使用二进制补码规则来转换负数