go中整数溢出不报错而是静默截断,如math.maxint64+1得math.minint64,uint下0-1得极大值;int平台相关易致跨平台问题;可用math.safeadd等函数运行时检测溢出。

Go 中 int 和 uint 溢出不报错,但结果不对
Go 的整数类型溢出时不会 panic,也不会自动转成大数,而是静默截断——这和 Python 或 JavaScript 完全不同。你写 math.MaxInt64 + 1,得到的不是错误,而是 math.MinInt64;0 - 1 在 uint 上直接变成极大值(比如 ^uint(0))。这种“无声失败”是多数线上整数计算 bug 的根源。
- 所有整数运算(
+、-、*、++、--)都可能溢出,且编译器默认不检查 -
int是平台相关类型(32 位机上是int32,64 位上是int64),跨平台时边界不一致,容易漏测 - 用
uint做索引或长度计算很常见,但一旦出现负偏移(比如i--后再用作切片下标),就会越界访问或 panic
怎么检测运行时溢出?用 math 包的 Safe* 函数
标准库 math 提供了 SafeAdd、SafeSub、SafeMul 等函数(Go 1.22+),它们返回 (result, overflow bool)。这是目前最轻量、无依赖的运行时防护手段。
- 只适用于
int、int8、int16、int32、int64和对应uint类型,不支持uintptr - 不能用于常量表达式(编译期就溢出的如
1),那些会直接编译失败 - 性能开销极小,但别在 hot path 上无脑套用——比如循环内反复判断同一变量是否溢出,应提前校验或换结构
示例:
sum, overflow := math.SafeAdd(a, b)<br>if overflow {<br> return errors.New("sum overflow")<br>}
什么时候该用 int64 而不是 int?
除非你明确需要平台适配(比如系统调用参数),否则优先用定长类型。Go 的 int 在 32 位环境只有 2³¹−1 上限,很多业务场景(时间戳毫秒、ID 生成、计费金额)稍不注意就超限。
立即学习“go语言免费学习笔记(深入)”;
- 数据库主键、Unix 时间戳(ms)、内存字节数、文件大小——一律用
int64 - 切片长度、数组索引、
len()结果虽是int,但若逻辑上可能超 2³¹,需显式转为int64再算,避免中间溢出 - 不要因为“省一个字母”写
for i := 0; i 就用 <code>int,如果s可能很长,len(s)本身没问题,但后续拿i做位移或乘法时就危险了
uint 不等于“更安全”,反而更容易掉坑
很多人以为用 uint 表示非负量更语义清晰,但 Go 的 uint 没有负数,也就没有“下溢”概念——0 - 1 得到的是极大正数,接着传给 make([]T, n) 或切片操作,立刻 panic: len out of range 或更隐蔽的越界读写。
- 切片索引、
for循环变量、偏移量计算——别用uint,哪怕注释写“保证非负”。因为任何减法、比较、接口转换都可能隐式触发翻转 - 函数参数如果是数量类(如
count int),接收方无法区分传入的是0还是溢出后的^uint(0),而int至少还能通过符号位发现异常 - 和 C/C++ 互操作时,C 的
size_t映射为uintptr更稳妥,而不是盲目用uint
溢出真正难防的点不在大数计算,而在类型混用和隐式转换——比如把 int64 赋给 int,或者 uint 和 int 直接相加。这些地方编译器不报错,运行时才露馅。










