C 语言隐式类型转换

前言

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 的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值