Appearance
题目
C 语言代码如下:
c
int i = 32777;
short si = i;
int j = si;执行上述代码段后,j 的值为( )。
错因
A
记得"超出 short 范围会变负",但误以为"绝对值不变、只翻符号"——直接把 32777 加上负号写成 −32777。这种"只翻符号"的直觉跳过了补码截断的实际机制:截断丢的是高位的位串,得到的负值需要按补码定义重新解读,并不是把数值直接镜像成 −x。
C
正确地完成了"32777 → 二进制 → 取低 16 位 → 0x8009"这一步,也算到了"取反加 1"得到 0x7FF7 = 32759,但忘了加负号——或者干脆把 si 当成无符号 short 读。截断后 si 的位串最高位 (第 15 位) 是 1,按 short 的有符号表示必须解读为负数。错点是"截断了但没看符号位"。
D
完全没意识到 32777 已经超出了 short 的范围。short 是 16 位有符号整数,范围 ,32777 已经溢出。选 D 的人多半把"int 转 short"误当作无损赋值——只要语法允许就以为值不变。一旦 short 装不下原值,就必须发生截断,结果就不再等于 i。
总解析
关键三步:
int → short:32 位 → 16 位的截断,保留低 16 位- short 是有符号类型,按补码解读,最高位 (第 15 位) 决定正负
short → int:16 位 → 32 位的符号扩展,高位补符号位,保持原有符号值不变
第一步:求 i 的二进制(int 32 位)
第二步:截断为 short si(保留低 16 位)
注意 si 最高位 (第 15 位) = 1 → 按 short 的有符号补码解读时是负数。
第三步:解读 si 的真值(补码 → 真值)
按补码定义直接展开:
或用"取反加 1"校验:
| 步骤 | 二进制 |
|---|---|
| si 位串 | 1000 0000 0000 1001 |
| 各位取反 | 0111 1111 1111 0110 |
| +1 | 0111 1111 1111 0111 = 0x7FF7 = 32759 |
| 加上负号 | −32759 |
两种算法结果一致。
第四步:si 赋给 int j(short → int,符号扩展)
si 是负数,符号位 = 1,扩展时高 16 位全补 1:
按 32 位补码解读,真值仍是 。符号扩展的本质就是保持有符号值不变——位串变了,但代表的数值没变。
最终答案是 B(−32759)。
类型转换三大易错点速记:
| 转换方向 | 行为 | 易错点 |
|---|---|---|
| 长 → 短(int → short) | 截断低位 | 忘了截断、或截断后不看符号位 |
| 短 → 长,有符号 (short → int) | 符号扩展(补符号位) | 误以为补 0 |
| 短 → 长,无符号 (unsigned short → int) | 零扩展(补 0) | 与有符号情况混用 |
口诀:截断看低位、扩展看符号——是"signed short"就按符号扩展,永远保值。