C++踩坑记录之隐式类型转换
本文最后更新于:2023年12月17日 下午
前言
直接从问题引入吧,请问下面这段代码的输出是什么?
1 |
|
隐式类型转换
如果你想当然地认为上面的输出是-4,那么显然你没有仔细地看过本文的标题(笑),要么就是你和我一样,火候还没有到家。那么输出结果到底是什么呢?见下图。
什么!?居然输出的是18446744073709551613这么一个大数字,为什么?
其实这里包含了两个知识点,第一,就是string
的length()
方法返回值类型是unsigned long
,其实当你看到输出这么大的一个数字时,就不难猜想到跟unsigned long类型相关。第二,就是C++中的隐式类型转换,上述 i - s.length()
的计算过程中,i
是int
类型,而 s.length()
是 unsigned long
类型,两者的类型不一致,所以会发生隐式类型转换,也就是把int
类型自动转换为 unsigned long
类型。
i
转换为unsigned long
类型后,数值为1,而 s.length()
数值为5,因此相减会发生下溢,但因为是unsigned long
类型,所以输出一个大正整数。如果将结果转换成long
类型,就可以看到预期输出的了。下面的代码和输出结果,可以帮助我们更好地理解这一过程。
1 |
|
顺带一提,int 类型往 unsigned long 转换时,对于正数,高位补0,对于负数高位补1。
接下来我们再来看一个例子,看看下面这段代码,最终的 id
会输出什么?
这里也不卖关子了,最后会输出 -1,而不是预期的 INT64_MAX / 2 的值。那么这里其实也是存在一个类型转换的问题,需要意识到字面量 1 ,它的类型实际上是 int
,因此在 MGet
函数模板识别 Value 类型的时候会识别成 int
,因此在函数返回时会做一次强制类型转换(int64_t
转换成 int
,进行截断), 然后再赋值给 id
时又做了一次隐式类型转换。那么第一次强制类型转换的结果,也就是函数返回值就是 -1,因为 INT64_MAX / 2 低32位全是 1,向下转型成 int
后,对应的数值就是 -1,而 int
向上转型到 int64_t
,高位补1,因此最终的数值还是表示 -1。
小结
最后小结一下,C++做为强类型语言,要求编译期的类型声明与检查,要求表达式中各操作数的类型(包括赋值操作的左值和右值,以及形参和实参)具有一致性,但同时也允许一定的灵活性,允许类型在一定程度上的兼容,也就是允许类型遵循一定规则下的隐式转换和强制转换[3]。
一些常见发生隐式转换的时机有:
- 在混合类型的表达式中,其操作数被转换为相同的类型。一般为低精度转高精度,就比如上面提到的 int 转 unsigned long。
- 用作条件的表达式被转换为 bool 类型。
- 用一表达式初始化某个变量,或将一表达式赋值给某个变量,则该表达式被转换为该变量的类型。
- 在函数调用中也可能发生隐式类型转换。