类型转换¶
方法速览¶
| 任务 | 方法 | 口诀 |
|---|---|---|
| 隐式转换方向 | char/short → int → unsigned int → long long → unsigned long long → float → double |
小类型让路给大类型 |
| 整数提升 | char/unsigned char/short/unsigned short 参与运算一律先转 int |
比int小的先变int |
| 有符号 ↔ 无符号混算 | 有符号先转无符号 | 混算见无符号,负数变巨数 |
| 强制转换 | (类型)表达式,浮点→整数向零截断 |
截尾不四舍五入 |
| 赋值降级 | 右侧结果转成左侧类型,超范围回绕 | 装不下就回绕 |
隐式类型转换链¶
同一表达式里出现不同类型时,低位类型自动向高位类型转换(没有信息损失的方向)。本节只讨论这十个类型:
(unsigned char、unsigned short 与 char、short 同级,都先提升成 int。)
三条规则:
- 整数提升:
char、unsigned char、short、unsigned short参与任何运算,先一律转成int——所以'a' + 1的结果是int的 98,不是char; - 逐级向上:
int遇double转double,long long遇float转float……混合运算的结果类型 = 链上更高的那个; - 赋值看左边:运算按链上最高类型算完,存回左边变量时再转成左边类型——装不下就回绕/截断。
| 表达式 | 过程 | 结果类型 |
|---|---|---|
'a' + 1 |
char 提升 int | int(98) |
5 / 2 |
全 int | int(2) |
5 / 2.0 |
int 转 double | double(2.5) |
3 + 2.5f |
int 转 float | float |
7ll * 2 |
int 转 long long | long long |
无符号陷阱:有符号与无符号混算时,有符号一方先转成无符号再算——负数的补码位串直接按无符号解读,变成巨大的正数:
(-1) < 1u→ \(-1\) 转成 \(4294967295\) → 判“假”;unsigned计数器永远 \(\ge 0\):for (unsigned i = 10; i >= 0; --i)是死循环——i = 0时--i回绕成最大值,条件恒真。
强制类型转换¶
写法:(类型)表达式 或 类型(表达式)。转换方向不受链约束,可以往低转——代价是丢信息:
- 浮点 → 整数:向零截断(直接砍掉小数部分,不是四舍五入):
(int)3.9 = 3、(int)(-3.9) = -3——负数也朝 0 方向砍,不是向下取整(\(-3.9\) 向下取整是 \(-4\),截断是 \(-3\)); - 大整数 → 小整数:保留低位字节(模 \(2^n\)):
char c = 300→ \(300 - 256 = 44\); - double → float:有效位数 16 → 7,精度丢失。
先除后转 vs 先转后除——括号位置决定结果:
真题演算¶
例(2019 S 卷):int a = 7; float x = 2.5, y = 4.7;,求 x + a % 3 * (int)(x + y) % 2。
| 步骤 | 片段 | 演算 | 结果 |
|---|---|---|---|
| 1 | (int)(x + y) |
\(2.5 + 4.7 = 7.2\),向零截断 | \(7\) |
| 2 | a % 3 |
\(7 \bmod 3\) | \(1\) |
| 3 | 1 * 7 % 2 |
同级左结合:\(1\times7=7\),\(7\bmod2\) | \(1\) |
| 4 | x + 1 |
int 转 float:\(2.5 + 1.0\) | \(3.5\) |
答案 \(3.5\)。三处考点:强转只作用于 (x+y) 一个括号;% * 同级左结合;最后 int 与 float 混算提升成 float。
练习题目¶
练习 1 · 整数提升¶
题目
cout << 'a' + 1; 输出( )
- A.
b - B. \(97\)
- C. \(98\)
- D.
a1
答案与解析
答案:C
char 参与运算先提升成 int:\(97 + 1 = 98\),按 int 输出。想输出 b 要写 char('a' + 1)。
练习 2 · 提升不限于 char¶
题目
short s = 32767; cout << s + 1; 输出( )
- A. \(-32768\)
- B. \(32768\)
- C. \(32767\)
- D. 溢出报错
答案与解析
答案:B
short 先提升成 int(4 字节),\(32767+1\) 在 int 里装得下 → 32768。A 是“直接用 short 算”的错误结果——提升让小类型运算不回绕。
练习 3 · unsigned char 提升后存回¶
题目
unsigned char u = 255; u = u + 1; cout << (int)u; 输出( )
- A. \(0\)
- B. \(256\)
- C. \(255\)
- D. \(-1\)
答案与解析
答案:A
分两步:u + 1 按 int 算 = 256;存回 u 时转成 unsigned char,\(256 \bmod 256 = 0\)。
干扰项思路:B 只看了提升、忘了存回;对比上一题——short 的 32768 装得进 int 所以保留,uchar 的 256 装不进 1 字节所以回绕。
练习 4 · 有符号 char 回绕¶
题目
char c = 127; c = c + 1; cout << (int)c; 输出( )
- A. \(128\)
- B. \(-128\)
- C. \(127\)
- D. \(0\)
答案与解析
答案:B
c+1 按 int 算 = 128;存回 char(8 位有符号),位串 10000000 按补码解读 = \(-128\)。
干扰项思路:A 忘了存回时降级。
练习 5 · 超范围赋值截断¶
题目
char c = 300; cout << (int)c; 输出( )
- A. \(300\)
- B. \(-44\)
- C. 编译错误
- D. \(44\)
答案与解析
答案:D
300 保留低 8 位:\(300 - 256 = 44\),44 在 \(-128\sim127\) 内 → 44。
练习 6 · 转换链排序¶
题目
下列类型按隐式转换从低到高排列正确的是( )
- A.
int→char→double - B.
char→int→double - C.
double→int→char - D.
char→double→int
答案与解析
答案:B
链:char/short → int → unsigned int → long long → unsigned long long → float → double。
干扰项思路:A、C、D 都把方向搞反或跳级。
练习 7 · 混合运算结果类型¶
题目
表达式 3 + 2.5 的结果类型是( )
- A.
int - B.
float - C.
double - D. 无法确定
答案与解析
答案:C
int 遇浮点提升;2.5 这类字面值默认是 double(带 f 后缀才是 float)→ 结果 double。
练习 8 · 5/2 与 5.0/2¶
题目
cout << 5 / 2 << " " << 5.0 / 2; 输出( )
- A.
2.5 2.5 - B.
2 2 - C.
2.5 2 - D.
2 2.5
答案与解析
答案:D
5 / 2 全 int → 整数除法得 2;5.0 / 2 时 5 转 double → 2.5。
干扰项思路:A 以为除号自动出小数——两边都是 int 就整除。
练习 9 · 整数除法链¶
题目
cout << 7 / 2 * 2; 输出( )
- A. \(7\)
- B. \(6\)
- C. \(4\)
- D. \(7.0\)
答案与解析
答案:B
/ * 同级左结合:\((7/2)=3\)(整除),\(3\times2=6\)。
干扰项思路:A 按先乘后除的直觉算成 7——同级运算从左到右。
练习 10 · 无符号混算¶
题目
cout << ((-2) < 1u ? "yes" : "no"); 输出( )
- A.
yes - B. 编译错误
- C.
no - D. 结果不确定
答案与解析
答案:C
\(-2\) 转成无符号:\(-2 + 2^{32} = 4294967294 > 1\) → 判假输出 no。
干扰项思路:A 按数学常识——混算规则先转无符号,负数变巨数。
练习 11 · 无符号循环陷阱¶
题目
for (unsigned i = 3; i >= 0; --i) cout << i; 循环( )
- A. 输出 3 2 1 0 后正常结束
- B. 输出 3 2 1
- C. 死循环
- D. 编译错误
答案与解析
答案:C
unsigned 的 i 永远 \(\ge 0\):i = 0 时 --i 回绕成 \(4294967295\),i >= 0 恒真 → 死循环。
干扰项思路:A 忽略回绕;倒序循环要么用 int,要么写 i != UINT_MAX。
练习 12 · 向零截断(正数)¶
题目
cout << (int)3.9; 输出( )
- A. \(3\)
- B. \(4\)
- C. \(3.9\)
- D. 编译错误
答案与解析
答案:A
浮点转整数向零截断——直接砍小数,不是四舍五入。
练习 13 · 向零截断(负数)¶
题目
cout << (int)(-3.9); 输出( )
- A. \(-4\)
- B. \(3\)
- C. \(-3.9\)
- D. \(-3\)
答案与解析
答案:D
向零截断:\(-3.9\) 朝 0 的方向砍成 \(-3\)。
干扰项思路:A 把“截断”当“向下取整”(floor)——floor 才是 \(-4\)。
练习 14 · 括号位置(先转后除)¶
题目
cout << (double)(5 / 2); 输出( )
- A. \(2.5\)
- B. \(2\)
- C. \(2.0\)
- D. 编译错误
答案与解析
答案:C
强转只作用于 (5 / 2) 的结果:先 int 除法得 2,再转 double → 2.0。
干扰项思路:A 以为强转能“挽救”已经丢掉的整数除法——想得 2.5 要写 (double)5 / 2。
练习 15 · 括号位置(先转后乘)¶
题目
(int)3.99 * 10 与 (int)(3.99 * 10) 的值分别是( )
- A. \(30\) 和 \(39\)
- B. \(39\) 和 \(30\)
- C. 都是 \(39\)
- D. 都是 \(30\)
答案与解析
答案:A
强转优先级高于 *:(int)3.99 先截断成 3,再 \(\times10 = 30\);后者先乘出 39.9 再截断 = 39。
练习 16 · char 的字符运算¶
题目
cout << '5' - '0'; 输出( )
- A.
50 - B. \(0\)
- C. \(5\)
- D.
'5'
答案与解析
答案:C
两个 char 都提升成 int:\(53 - 48 = 5\)——“数字字符转数值”的标准手法。
练习 17 · bool 提升¶
题目
cout << true + true; 输出( )
- A.
truetrue - B. \(2\)
- C. \(1\)
- D.
true
答案与解析
答案:B
bool 参与算术提升成 int:\(1 + 1 = 2\)。
练习 18 · long long 装大和¶
题目
int a = 1000000000, b = 1000000000; 要正确输出 \(a + b\),应写( )
- A.
cout << a + b; - B.
cout << (long long)a + b; - C.
cout << (double)a + b; - D.
cout << a * 2;
答案与解析
答案:B
\(a+b\) 按 int 算先溢出;先把 a 强转 long long,加法提升成 long long → \(2\times10^9\) 正确。
干扰项思路:A 溢出成负数;C 能装下但整数用浮点会丢精度,不是正解。
练习 19 · sizeof 表达式的类型¶
题目
sizeof(1 + 1.5) 的值是( )
- A. \(4\)(int 的大小)
- B. \(12\)(两者之和)
- C. 编译错误
- D. \(8\)(double 的大小)
答案与解析
答案:D
1 + 1.5 的结果类型是 double(提升),sizeof 量的是结果类型的大小 → 8。
练习 20 · float 的存储精度¶
题目
float f = 16777217; 则 f 实际存储的值是( )
- A. \(16777217\)
- B. \(16777216\)
- C. \(16777218\)
- D. \(16777200\)
答案与解析
答案:B
float 尾数 24 位,最多精确区分 \(2^{24} = 16777216\) 个整数——\(2^{24}+1\) 是第一个存不下的整数,就近舍入成 \(2^{24}\)。
记忆锚点:float 能精确到 1677 万。
练习 21 · 赋值降级(浮点→整型)¶
题目
double d = 3.99; int x = d; cout << x; 输出( )
- A. \(4\)
- B. \(3.99\)
- C. 编译错误
- D. \(3\)
答案与解析
答案:D
赋值降级:右侧 double 向零截断成 3。
练习 22 · 字符与整数的互换¶
题目
(char)('A' + 2) 的值是( )
- A.
'C' - B.
'B' - C. \(67\)
- D.
'A2'
答案与解析
答案:A
'A' = 65,\(65+2 = 67\),强转回 char → 'C'。
干扰项思路:C 忘了强转回 char(那是 ('A'+2) 不加转换的输出)。
练习 23 · 表达式的中间截断¶
题目
int x = 9; double y = x / 4; cout << y; 输出( )
- A. \(2.25\)
- B. \(2\)
- C. \(3\)
- D. \(2.5\)
答案与解析
答案:B
x / 4 两边都是 int → 整除得 2,再赋给 double 变 2.0。右边已经是截断后的值,左边接不住丢掉的小数。
干扰项思路:A 以为赋给 double 就会按小数除——先除后转,顺序不能反。
练习 24 · 累加器类型¶
题目
double s = 0; s += 1 / 3; s += 1 / 3; s += 1 / 3; cout << s; 输出( )
- A. \(1\)
- B. \(0\)
- C. \(0.999999\)
- D. \(0.333333\)
答案与解析
答案:B
每个 1 / 3 都是 int 整除 = 0,加三次还是 0。想得 1.0 要写 1.0 / 3。
干扰项思路:D 把每次整除想成 0.33……——int 除法在表达式内部就截断了,轮不到 double 接。
练习 25 · 强转与取模综合¶
题目
int t = (int)7.9 + 9 % 4; cout << t; 输出( )
- A. \(8\)
- B. \(9\)
- C. \(10\)
- D. \(11\)
答案与解析
答案:A
优先级:强转 > %(3 级)> +(4 级)——(int)7.9 = 7,9 % 4 = 1,\(7 + 1 = 8\)。
干扰项思路:B 把 7.9 四舍五入成 8 再加 1(或忘了截断按 7.9 + 1 估)——截断不四舍五入。
练习 26 · 综合求值¶
题目
cout << 3 + 4 / 2.0 + (int)2.9; 输出( )
- A. \(7\)
- B. \(8\)
- C. \(8.5\)
- D. \(7.5\)
答案与解析
答案:A
分步:4 / 2.0 → double 除法 = 2.0;(int)2.9 截断 = 2;\(3 + 2.0 + 2 = 7.0\),cout 输出 7。
干扰项思路:C 把 (int)2.9 当 3、又把 4 / 2.0 当 2.5(\(3+2.5+3=8.5\));B 把 4 / 2.0 当整除 2、(int)2.9 当 3(\(3+2+3=8\))。
历年真题¶
2019 年 · S 卷第 1 题¶
题目
若有定义:int a=7; float x=2.5, y=4.7;,则表达式 x+a%3*(int)(x+y)%2 的值是:( )
- A.
0.000000 - B.
2.750000 - C.
3.500000 - D.
2.500000
答案与解析
答案:C
演算见上文“真题演算”节:(int)(x+y) 截断成 7,a%3=1,1*7%2=1,2.5+1=3.5。强转只管一个括号、%/* 同级左结合、最后提升成 float——三步都不能错。
2024 年 · 第 8 题¶
题目
在 C/C++ 中,(char)('a'+13) 与下面的哪一个值相等?( )
- A.
'm' - B.
'n' - C.
'z' - D.
'3'
答案与解析
答案:B
'a' = 97,先提升成 int 加 13 得 110,强转回 char → 'n'(97 是 a,110 是 n——a 到 m 差 12,到 n 差 13)。
干扰项思路:A 差一位(a+12 才是 m)——ASCII 数到字符时极易偏一。
易错小结¶
- 提升方向:
char/short→int→unsigned→long long→float/double;小类型运算先升 int,升完不回绕,但存回小类型时会回绕(char c = 300→ 44); - 无符号陷阱:混算负数变巨数;unsigned 计数器写
i >= 0是死循环; - 截断 ≠ 四舍五入:
(int)3.9 = 3、(int)(-3.9) = -3——负数朝 0 砍,不是 floor; - 强转只管紧挨着的操作数:
(double)(5/2)是 2.0,(double)5/2才是 2.5——括号位置改变一切; - 整除发生在表达式内部:
x / 4两边都 int 就先整除,赋给 double 也接不回小数; - 大数加法先转 long long:
(long long)a + b,别让 int 先溢出; - 浮点字面值默认 double:
3 + 2.5的结果类型是 double,不是 float。