C/C++中奇妙的类型转换

1.引言

大家在学习C语言的时候,有没有遇见过类似于下面这样的代码呢?

// 整形转bool
int count = 10;
while(count--)
{
	cout << count << endl;
}

// 指针转bool
int* ptr = cur;
while(ptr)
{
    //......
}

众所周知,while循环的判断是bool类型的变量,那为什么整形变量和指针变量可以直接作为 while循环的判断条件呢?之所以可以这样直接利用整形变量和指针变量作为while循环的判断条件,是不是说明整形类型的变量和指针类型的变量可以转换成bool类型呢?没错,是的。

大家在学习编程的过程中肯定都有遇到过各种各样的 "神奇的" 类型转换,这些类型转换给我们编程带来了很大的方便,比如说 上面的整形直接转bool值判断、指针直接转bool值判断等等......下面我们就一起来学习一下C/C++中的那些神奇的类型转换。

提醒:所有的类型转换之间要有关联,没有关联的类型之间即使是强制类型转换,也是不能转换的。

2.C语言中的那些类型转换

在C编写C/C++代码的时候,我们经常会遇到发生类型转换的场景,比如 赋值运算符的两个操作数不同实参和形参类型不同函数返回值类型和接收返回值的类型不同,都会发生类型转换;所以,在C语言中提供了两种类型转换 ------ 隐式类型转换和显示类型转换

隐式类型转换

隐式类型转换是隐式的,是我们看不见的,比如下面这段代码:

double a = 1.0;
int b = a; // 发生隐式类型转换

隐式的类型转换有以下几种:

  • 1.整形和整形之间:不同的整形之间是可以发生隐式类型转换的,比如:char,short,int,long 、long long之间(char是属于整形家族的哦!)。
  • 2.整形和浮点型之间:整形和浮点型数据之间也具有一定的关联性,也是可以发生隐式类型转换的,比如:int 和 double,int 和 float 类型的数据......
  • 3.整形和bool之间:因为在编程中,我们习惯用0表示假,非0表示真,所以整形和bool型的数据是可以相互转换的。
  • 4.指针和bool之间:指针有可以分为空指针和非空指针,相当于0和非0,所以指针类型的数据也是可以和bool型之间的数据进行转换的。

显示类型转换

显示类型转换式可以看见的,是用户显示使用的,比如下面这段代码:

int main(){
	int c = 0;
	char* pc = (char*)&c;
	 
	return 0;
}

显示的类型转换有以下几种:

  • 整形和指针类型之间:这两者之间可以转换是因为指针是进程地址空间中字节的编号,和整形数据之间还是具有关联性的,所以可以互相转换。
  • 不同类型的指针变量之间:之所以可以互相转换,和上面一点是相同道理的;但是指针的类型决定了指针解引用之后,可以访问的内存地址字节数的大小(比如说,int*类型的指针解引用之后,可以访问四字节的内存空间,char*类型的指针解引用之后,只能访问1字节的内存空间;这是由指针所指向的数据的类型决定的)。有了这个点,其实所有类型的对象之间都能间接转换了,但是解引用之后所能访问的内存空间大小不一样

3.C++中的那些类型转换

为什么C++还要改进类型转换呢?

我们都知道,C++是对C的改进和扩充,C++不仅仅改进了C语言中的错误处理机制,还改进了C语言中的类型转换。相信你可能有这样的疑问,C语言中的类型转换挺好用的,那为什么还要改进呢?看下面这段代码:

#include <iostream>
using namespace std; 

void func(size_t pos)
{
	int end = 10;
	while (end >= pos)
	{
		cout << end << endl;
		--end;
	}
}

int main()
{
	func(0);
	return 0;
}

大家可以猜一猜上面这段程序运行的结果是什么?结果如图:

  • 出乎意料吧,程序的结果是死循环;这是因为,end 和 pos是不同类型的数据,相互比较时会发生隐式的类型转换,范围小的会向范围大的数据类型转换,所以end会转换为无符号数,无符号数中没有负数,所以比较的时候,end总是大于pos,导致死循环。

从上面这个案例可以看出,C语言中的类型转化可视性比较差,可能存在难以预料的风险,所以C++的前辈们觉得有必要对类型转换升级升级了。

C++中的类型转换

C++语言中引入了面向对象的思想,提供了继承机制,所以多多少少都会涉及父类和子类之间的转换,这是C语言中的类型转换不能很好的解决的问题之一;再者,C语言中的类型转换可视性较差,所以C++中为了加强类型转换的可视性和可控性,提供了四种类型转换操作符,分别是 static_cast、reinterpret_cast、const_cast、dynamic_cast;

static_cast:static_cast 操作符对应C语言中的隐式类型转换,使用方式如一下代码,加强了隐式类型转换的可视性。

int main()
{
	double a = 3.14;
	int b = static_cast<int>(a);
	
	return 0;
} 

reinterpret_cast:reinterpret_cast 操作符对应C语言中的强制类型转换,使用方式如下,以前使用强制类型转换的地方就可以这样使用了。

int main()
{
	int a = 0;
	int* p = reinterpret_cast<int*>(a);
	
	return 0;
} 

const_cast:const_cast 操作符用于强制类型转换中,去掉const属性 。去掉变量的const属性的这种强制类型转换存在一定的内存可视化风险,虽然强制类型转换也可以去掉const属性,但是可视性较差,不便于分析程序。

  • 分析上面的代码可以得出,a变量并没有被重新赋值,但是*p指向的空间被重新赋值了,但是*p指向的不就是变量a吗?为什么会出现这样的现象呢?这是因为,编译器认为a是const修饰的,是不会改变的变量,所以在寄存器上存放了一份a,这样一来,当需要使用a变量的时候,直接就可以去寄存器上面取,提高程序的运行效率,*p = 3;改变的是内存空间中a的值,打印的时候,a是在寄存器中取的值,*p是在内存空间中取的值,所以两个值不一样。
  • 分析上面代码可以看出给 const 修饰的变量添加volatile关键字,答应出的结果在意料之中,这是因为,volatile关键字表明,不把该 const 修饰的变量 a 放在寄存器中,所以不会出现上面那种情况。

dynamic_cast:dynamic_cast操作符用于支持向下转换,也就是将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换);dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0;dynamic_cast只能用于父类含有虚函数的类,这是因为,dynamic_cast的实现和虚函数表有关,要想有虚函数表,类中得有虚函数。

  • 如上图左侧所示,如果父类的指针指向子类对象,该父类的指针是可以转化成子类的指针的,因为可以看到本来就属于它的空间
  • 如上图右侧所示,如果父类的指针指向父类对象,该父类的指针是不能转化为子类的指针的,因为会导致解引用之后,该指针的步长变大,访问本来就不属于它的空间。
  • 总结一下就是子类的指针or引用or对象,是可以直接转换成父类的指针or引用or对象(赋值兼容转换规则),但是父类的指针or引用需要通过dynamic_cast操作符类进行转换,如果能转换,则转换,反之,则返回0;
相关推荐
erxij4 分钟前
【游戏引擎之路】登神长阶(十四)——OpenGL教程:士别三日,当刮目相看
c++·经验分享·游戏·3d·游戏引擎
林开落L34 分钟前
前缀和算法习题篇(上)
c++·算法·leetcode
Prejudices1 小时前
C++如何调用Python脚本
开发语言·c++·python
单音GG1 小时前
推荐一个基于协程的C++(lua)游戏服务器
服务器·c++·游戏·lua
qing_0406031 小时前
C++——多态
开发语言·c++·多态
孙同学_1 小时前
【C++】—掌握STL vector 类:“Vector简介:动态数组的高效应用”
开发语言·c++
charlie1145141912 小时前
Qt Event事件系统小探2
c++·qt·拖放·事件系统
iiiiiankor2 小时前
C/C++内存管理 | new的机制 | 重载自己的operator new
java·c语言·c++
小辛学西嘎嘎2 小时前
C/C++精品项目之图床共享云存储(3):网络缓冲区类和main
c语言·开发语言·c++
c语言鹌鹑蛋2 小时前
C++初阶 --- 类和对象(1)
开发语言·c++