前言
C 语言隐式执行的类型转换,让我们难以捉摸。有时默默进行的类型转化会导致难忘的经历,我曾经就因 -1 > 1 这个表达式为真,找了一个多小时的 bug。你可能会想 -1 怎么可能大于 1,别着急先来看下面这段代码,它的输出是什么?
#include <stdio.h>
int main() {
if (-1 < (unsigned char)1) {
printf("-1 < (unsigned char)1\n");
}
if (-1 > (unsigned int)1) {
printf("-1 > (unsigned int)1\n");
}
return 0;
}
编译器版本:gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04)
它的输出是:
-1 < (unsigned char)1
-1 > (unsigned int)1
是不是有点懵了,怎么大小还和类型有关。在回答这个问题之前,先来看两个背景知识。
整型提升
C 语言的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
K&R C A.6.2 整型提升
在一个表达式中,凡是可以使用整型的地方都可以使用带符号或无符号的字符、短整型或整型位段,还可以使用枚举类型的对象。如果原始类型的所有值都可用 int 类型表示,则其值将被转换为 int 类型,否则将被转换为 unsigned int 类型。这一过程称为整型提升(integral promotion)。
整型提升的意义:
表达式的整型运算要在 CPU 的相应运算器件内执行,CPU 内整型运算器(ALU)的操作数的字节长度一般是 int 的字节长度,同时也是 CPU 的通用寄存器的长度。
通用 CPU(general-purpose CPU)难以直接实现两个 8 比特直接相加运算。所以,表达式中各种长度可能小于 int 长度的整型值,都必须先转换为 int 或 unsigned int,然后才能送入 CPU 去执行运算。
sign extension 是怎么进行的?又怎么保证整数值不发生变化?
sign extension 会在前面填充符号位。
对于正数,前面添加 0 不影响大小。
对于负数,比如将 -6 的 5 位补码( 11010 11010 11010)扩展为 8 位,变为 11111010 11111010 11111010。
为什么这两个数是相同的呢?是什么原理?
扩展到第 6 位时,原本第 5 位的权值由 -8 变为 8,第 6 位权值又为 -16,刚好抵消,值不发生变化,后面以此类推。
算术转换
K&R C A.6.5 算术类型转换
许多运算符都会以类似的方式在运算过程中引起转换,并产生结果类型。其效果是将所有操作数转换为同一公共类型,并以此作为结果的类型。这种方式的转换称为普通算术类型转换。
首先,如果任何一个操作数为 long double 类型,则将另一个操作数转换为 long double 类型。
否则,如果任何一个操作数为 double 类型,则将另一个操作数转换为 double 类型。
否则,如果任何一个操作数为 float 类型,则将另一个操作数转换为 float 类型。
否则,同时对两个操作数进行整型提升;然后,如果任何一个操作数为 unsigned long int 类型,则将另一个操作数转换为 unsigned long int 类型。
否则,如果一个操作数为 long int 类型且另一个操作数为 unsigned int 类型,则结果依赖于 long int 类型是否可以表示所有的 unsigned int 类型的值。如果可以,则将 unsigned int 转换为 long int;如果不可以,则将两个操作数转换为 unsigned long int 类型。
否则,如果一个操作数为 long int 类型,则将另一个操作数转换为 long int 类型。
否则,如果任何一个操作数为 unsigned int 类型,则将另一个操作数转换为 unsigned int 类型。
否则,将两个操作数都转换为 int 类型。
如果某个操作符的各个操作数属于不同的类型,除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。
解答
下面就回答开始提出的问题:
if (-1 < (unsigned char)1) {
printf("-1 < (unsigned char)1\n");
}
unsigned char 类型的所有值都可以用 int 表示,所以会提升至 int 类型。int 类型的 -1 小于 1 合情合理。
if (-1 > (unsigned int)1) {
printf("-1 > (unsigned int)1\n");
}
这个又有些不同,两个值都不需要整型提升。但两个操作数的类型不同,需要进行算术转换,根据上面的规则两个操作数都被转换为 unsigned int。而 -1 的补码为全 1,这在无符号数中是最大值,当然是大于 1 的。
251

被折叠的 条评论
为什么被折叠?



