C 类型转换 — 整型提升与算术转换

← C语言知识地图 | ← 主页


整型提升(Integer Promotion)

任何 rank 低于 int 的类型,在表达式中先自动提升为 int(或 unsigned int)。

触发时机:

  • 算术运算、比较运算、位运算的操作数
  • 函数调用的参数(无原型时还要受 default argument promotion 再处理一次)
char  a = 10;
short b = 20;
// a + b 实际按 int + int 计算,结果类型为 int

char 是否有符号由实现决定 — 嵌入式常见 unsigned char,x86 上通常 signed char。 提升时若原始类型的所有值都能放进 int,提升到 int;否则提升到 unsigned int


寻常算术转换(Usual Arithmetic Conversions)

二元运算的两个操作数类型不同时,把”小”的一方转为”大”的一方的类型。

整数转换 Rank(由低到高)

Rank类型
1_Bool
2char, signed char, unsigned char
3short, unsigned short
4int, unsigned int
5long, unsigned long
6long long, unsigned long long

转换规则(简化)

  1. 先做整型提升(低于 int 的提到 int/unsigned int
  2. 若类型仍不同,rank 低的向 rank 高的转
  3. 关键 — unsigned “赢”:同 rank 的 signed 与 unsigned 相遇,signed → unsigned
int a = -1;
unsigned int b = 1;
 
if (a > b)  // 成立!因为 a 被转为 unsigned int,-1 → 0xFFFFFFFF(4294967295)
    ...

这条规则不限于同 rank:当 unsigned 操作数的 rank ≥ signed 操作数的 rank,signed 被转为 unsigned。

int a = -5;
unsigned short b = 10;  // 先提升为 int → 10,但...
 
int c = -5;
unsigned int d = 10;
// c + d → c 转为 unsigned int,-5 变成 0xFFFFFFFB,加法结果溢出

🔴 翻车场景

1. 有符号减无符号 → 永远 ≥ 0

unsigned int len = 5;
int data_len = 3;
 
if (data_len - len > 0)   // ❌ 你以为为 false,实际为 true
if (data_len > len)        // ✅ 直接比,两边按 unsigned 比 → false

data_len - len 的结果是无符号数,不可能 < 0。

2. sizeof 返回 size_t(unsigned)

int a[10];
for (int i = 0; i < sizeof(a) / sizeof(a[0]) - 1; i++)  // ⚠️ 混合了 int 和 size_t

sizeof 返回 size_tunsigned long),比较时 i 被转为 size_t。这里不会出错,但换一种写法就翻车:

for (size_t i = 9; i >= 0; i--)  // ❌ 死循环!i 永远 ≥ 0

size_t 是无符号的,i == 0i-- 变成 SIZE_MAX,仍然 ≥ 0。

3. 循环边界用 unsigned 导致死循环

// ❌ 死循环
for (unsigned int i = n; i >= 0; i--)
    ...
 
// ✅ 修正
for (int i = n; i >= 0; i--)
    ...
// 或用后判法:
for (unsigned int i = n + 1; i-- > 0; )
    ...

4. 位运算结果默认 unsigned

unsigned char x = 0xFF;
// x 先整型提升为 int(值 255),不是 unsigned int
// 0xFF << 24 在 int 为 32 位的系统上可能溢出 → 未定义行为

位运算操作数建议显式用 uint32_t 等定宽类型。


规避原则

场景做法
比较、运算中不用无符号能用 int 就用 int,除非真的需要模 2ⁿ 回绕语义
必须用无符号时uint32_t 等定宽类型,明确意图
混用时显式转换:if ((int)u < 0) 而不是等隐式转换
循环绝不写 for (unsigned i = n; i >= 0; i--)
位运算统一用 uint8/16/32_t,避免提升意外