上周线上出个诡异 bug,一个倒计时算到零,下一秒显示成四十多亿,客服以为数据炸了。其实就一行,无符号整型被减成了负值,它没有负的概念,直接从最大值绕回来。
unsigned int 就是不带符号的整数类型,前面的 unsigned 告诉编译器别拿最高位表示正负,全部拿来存数值。同样 32 位,普通 int 能存到二十一亿多,它能存到四十二亿多,代价是装不下任何负数。
混用有符号数时比较会翻车
这是被坑最多的一块。拿一个 unsigned int 跟负一比较大小,编译器会把负一转成一个巨大的无符号数,结果变成比谁都大,if 直接走反分支。开启 Wall 编译选项能看到警告,但很多人随手关掉,下次上线就暴雷。
还有循环里的 i 减一场景。i 是无符号数,减到零再减,直接绕回四十二亿,for 循环永远停不下来。写倒计时、写分页偏移时,这种地方最危险。
什么时候用它什么时候别碰
存内存大小、数组长度、端口号、像素值这些天然非负的量,用它挺合适,天然挡掉负值,表达意图也清楚。价格、温度、坐标、余额增减这些可能出现负数的,一律老老实实用 int 或者 long long,别为了那点多出来的上限冒险。
unsigned int n = 5; n = n - 10; // n 变成 4294967291- 做减法前:先判断会不会越过零,越过就先拦截
- 做比较时:绝不直接跟有符号数比,先统一类型
- 打印输出:占位符用百分号加 u,用错会打出奇怪的数
再看个具体场景,分页查询算偏移量,页码减一乘每页条数。页码正常是正数,没事;但用户手动把地址栏里的页码改成零,减一就成了负一,乘完变成个巨大数,SQL 里的 offset 直接撑爆,查出来一堆乱七八糟的行。前端传参必须校验下限,后端也别信前端。
还有一种隐蔽的,位运算右移。无符号数右移是逻辑右移,左边补零;有符号数是算术右移,左边补符号位。同一个数用不同类型做右移,结果可能差出天壤之别,做哈希、做位图索引时尤其要先想清楚声明什么类型。
一句话:它多出来的是上限,代价是负数会变成天文数字,只要业务里可能出现减到零以下,就别用它。