C++踩坑记录之隐式类型转换

本文最后更新于:2023年12月17日 下午

前言

直接从问题引入吧,请问下面这段代码的输出是什么?

1
2
3
string s = "Hello";
int i = 1;
cout << i - s.length() << endl;

隐式类型转换

如果你想当然地认为上面的输出是-4,那么显然你没有仔细地看过本文的标题(笑),要么就是你和我一样,火候还没有到家。那么输出结果到底是什么呢?见下图。

奇怪的输出结果

什么!?居然输出的是18446744073709551613这么一个大数字,为什么?

其实这里包含了两个知识点,第一,就是stringlength() 方法返回值类型是unsigned long,其实当你看到输出这么大的一个数字时,就不难猜想到跟unsigned long类型相关。第二,就是C++中的隐式类型转换,上述 i - s.length() 的计算过程中,iint类型,而 s.length()unsigned long 类型,两者的类型不一致,所以会发生隐式类型转换,也就是把int 类型自动转换为 unsigned long 类型。

i 转换为unsigned long 类型后,数值为1,而 s.length() 数值为5,因此相减会发生下溢,但因为是unsigned long 类型,所以输出一个大正整数。如果将结果转换成long类型,就可以看到预期输出的了。下面的代码和输出结果,可以帮助我们更好地理解这一过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<iostream>
#include<string>
using namespace std;

// 输出二进制表示
void print(unsigned long num) {
cout << "binary:";
for (int i=63;i>=0;i--) {
cout << ((num >> i ) & 1);
}
cout << endl;
}

int main() {
string s = "Hello";
int i = 1;

cout << "i\t\t\t";
print(i);

unsigned long u_i = static_cast<unsigned long>(i);
cout << "u_i\t\t\t";
print(u_i);

cout << "s.length()\t";
print(s.length());

cout << "i - s.length() : ";
print(i-s.length());
cout << "i - s.length() : " << i - s.length() << endl;

return 0;

}

输出结果

顺带一提,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 类型。
  • 用一表达式初始化某个变量,或将一表达式赋值给某个变量,则该表达式被转换为该变量的类型。
  • 在函数调用中也可能发生隐式类型转换。

备注/参考


C++踩坑记录之隐式类型转换
https://2017zhangyuxuan.github.io/2022/04/10/2022-04/2022-04-10 C++踩坑记录之隐式转换/
作者
Zhang Yuxuan
发布于
2022年4月10日
许可协议