Go中int和uint互转的坑

2025 年 1 月 3 日 星期五(已编辑)
12
这篇文章上次修改于 2025 年 1 月 14 日 星期二,可能部分内容已经不适用,如有疑问可询问作者。

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的范围内,但是使用中间变量之后,会在运行时进行类型转换,这时候可以使用二进制补码规则来转换负数

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...