03 整数提升、有符号与无符号溢出
1. 整数提升
在 C 语言中,char、short 等较窄整数类型参与多数算术运算前,会先发生整数提升:如果 int 能表示该类型的所有值,就提升为 int;否则提升为 unsigned int。
uint8_t a = 200, b = 100;
int x = a + b; // 300(常见 32 位 int 平台)
uint8_t y = a + b; // 44:300 % 256
要区分表达式的计算类型与赋值后的目标类型。例如,uint16_t 在常见 32 位 int 平台上参与乘法时,也通常先提升为 int。
2. 有符号与无符号溢出
| 类型 | 超出范围时 | C 语言规则 |
|---|---|---|
| 无符号整数 | 按 2^N 取模回绕 | 有定义,但可能造成逻辑错误 |
| 有符号整数 | 溢出 | 未定义行为(UB),不能依赖回绕结果 |
以 uint8_t 为例,范围为 0~255:
uint8_t n = 255;
n++; // 0
n--; // 255
无符号整数减法同样可能回绕。注意:无符号转换的回绕与中间有符号表达式溢出是两回事;把结果赋给无符号变量,并不会修复之前已经发生的未定义行为。
3. 有符号与无符号混合运算
混合运算会执行通常算术转换,确定共同的运算类型。不能仅凭数值直觉判断结果。
在 int 和 unsigned int 均为 32 位的环境中,二者混合运算时,int 会转换为 unsigned int。因此负数参与比较或计算时,可能转为很大的无符号值。
分析顺序:先看整数提升 → 再看通常算术转换 → 确认运算类型及范围 → 最后看赋值转换。
4. 缓冲区容量检查
直接写 used + len <= capacity 可能因为无符号加法回绕而误判容量足够。
if (used <= capacity && len <= capacity - used) {
// 容量足够
}
先检查 used 没有超过容量,再用剩余空间判断;利用 && 短路求值避免非法减法产生回绕。这种写法适合数据包解析和缓冲区追加操作。
5. 计数器回绕
无符号计数器发生一次回绕时,可以用同类型的无符号减法计算两次采样之间的差值。前提是两次采样间实际增量小于一个完整计数周期;超过或等于一圈就无法仅靠差值还原真实次数。
核心总结
- 小整数参与运算时,可能先提升为
int,不能直接按原类型位宽推算。 - 无符号整数回绕有定义;有符号整数溢出是未定义行为。
- 无符号算术也要先判断是否真的越界,并警惕减法下溢。
- 检查缓冲区容量时,优先采用“先验证已用长度,再比较剩余容量”。