strconv.AppendInt 避免中间字符串分配,直接原地追加字节;而 fmt.Sprintf 和 strconv.Itoa 均需额外分配和拷贝;其 base 仅安全支持 2/8/10/16,非法值行为未定义;返回新切片头,必须接收以避免写入失效内存;负数自动加 '-',多占 1 字节。

为什么用 strconv.AppendInt 而不是 fmt.Sprintf 或 strconv.Itoa
直接拼接数字到已有 []byte 缓冲区时,strconv.AppendInt 避免了中间字符串分配和拷贝——它不生成新字符串,而是原地追加字节。而 fmt.Sprintf 必须构造完整字符串再转 []byte,strconv.Itoa 返回新字符串后还需调用 append(buf, []byte(s)...),多一次内存分配和复制。在高频日志、序列化或网络编码场景中,这会导致明显 GC 压力和延迟毛刺。
strconv.AppendInt 的 base 参数必须是 2、8、10、16
传入非法进制(比如 base = 12)不会报错,但行为未定义:可能截断、乱码,甚至 panic(取决于 Go 版本和输入值)。实际使用中几乎只用 10(十进制)和 16(十六进制),前者用于常规输出,后者常用于调试或二进制协议中的紧凑表示。
- 十进制:
strconv.AppendInt(buf, 12345, 10)→ 追加[]byte("12345") - 十六进制小写:
strconv.AppendInt(buf, 255, 16)→ 追加[]byte("ff") - 十六进制大写需手动转换字符,
AppendInt不支持Upper标志
注意缓冲区容量与返回值,别忽略 append 的重分配逻辑
strconv.AppendInt 返回的是新的切片头(可能指向新底层数组),它不保证复用原 buf 底层空间。如果原 buf 容量不足,内部会调用 make([]byte, ...) 分配更大空间,此时你必须用返回值,否则后续操作会写到已失效的旧底层数组上。
buf := make([]byte, 0, 32) buf = strconv.AppendInt(buf, 999999999999, 10) // 必须接收返回值 buf = append(buf, 'x') // 正确:基于最新 buf 操作 // 错误写法:strconv.AppendInt(buf, ..., 10); buf = append(buf, 'x') —— 可能覆盖错误位置
负数处理是自动的,但符号位占额外字节
strconv.AppendInt 对负数自动添加 '-' 前缀,不需要额外判断。但要注意:负数比对应正数多占用 1 字节,例如 -1 写入 2 字节("-1"),而 1 只占 1 字节。在固定长度协议或内存敏感场景(如嵌入式序列化)中,这点会影响缓冲区预估大小和对齐计算。
立即学习“go语言免费学习笔记(深入)”;
如果你确定数值非负,且想省掉符号检查开销,可先断言 if n ,但这通常不必要——函数本身的符号处理成本极低,远低于一次额外的分支预测失败代价。
真正容易被忽略的是:当反复追加多个数值时,缓冲区容量增长不可控,建议初始化时按最大可能长度预留(例如 64 字节足够容纳 int64 十进制 + 符号 + 分隔符),避免多次扩容。


















