C/C++类型转换(C++四大强制类型转换) 目录C语言中的类型转换C中的类型转换C新增的四种强制类型转换static_castdynamic_castreinterpret_castconst_castC语言中的类型转换实验过程在下述代码中我穷举了一些指针与指针之间的隐式类型转换以及基础类型与基础类型的隐式类型转换的例子在VS2022下运行并没有产生任何错误,程序可以正常运行仅仅是爆出不少警告//test.c #define _CRT_SECURE_NO_WARNINGS #includestdio.h int main() { int a 0; char b a; double c 5.5; a b; a c; b c; b a; c a; c b; int* pa a; char* pb b; double* pc c; pa pb; pa pc; pb pa; pb pc; pc pa; pc pb; }还有就是在C语言中任意指针类型都可以隐式转换成void*类型而void*类型也可以隐式转换成任意指针类型在下面的代码中我又穷举了一些基础类型与指针之间的隐式类型转换的例子但这次出现了error的例子#define _CRT_SECURE_NO_WARNINGS #includestdio.h int main() { int a 0; char b a; double c 5.5; int* pa a; char* pb b; double* pc c; a pa; a pb; a pc; b pa; b pb; b pc; c pa;//error c pb;//error c pc;//error pa a; pa b; pa c;//error pb a; pb b; pb c;//error pc a; pc b; pc c;//error }其实仔细观察一下凡是出现error的例子都是double类型与指针类型相互转换的情况注意double*和double可没关系奥double*就是指针类型。实际上double包括float这些小数类型的数据在内存中的存储格式都与指针类型很不一样。浮点数重新解释为指针是危险且无意义的c禁止这样的转换实际上就算使用强制类型转换编译也不会通过总之就是不让你转换。uintptr_t和intptr_t下面我将简单介绍一下C99引入的uintptr_t和intptr_tuintptr_t和intptr_t本质上是内置类型的重命名也就是typedef比如uintptr_t是usigned long的重命名intptr_t可能是long的重命名所以这两货重命名的对象有什么规律呢实际上uintptr_t和intptr_t总是一个“与指针类型占用字节数相同却是一个整型类型”的数据类型的重命名也就是说在32位系统下(指针大小是4字节)他两分别是usigned long和long;在16位系统(指针大小是2字节)下他两分别是usigned int 和 int。总的来说uintptr_t和intptr_t就是与指针大小相同的整型类型。可以预见这样的话想把一个指针转成整型类型时就可以先将其转换成uintptr_t或者intptr_t如此就可以保证这个指针的完整性没有截断至于之后再把它转换成什么类型的整型那就属于整型类型相互转换的范畴了。而且在我们想把其转换回指针类型时通过uintptr_t或者intptr_t也可以完整的转换过去当然前提是你不能已经把它截断或者提升什么的。因此当我们想要指针与整型相互转换时可以用uintptr_t和intptr_t过渡最起码防止你在不经意间丢失了数据更加安全。这是关于uintptr_t和intptr_t的文章推荐【嵌入式】指针与整数的桥梁、跨平台编程的黄金钥匙揭开 uintptr_t 和 intptr_t 的神秘面纱-CSDN博客double和指针类型互转介绍一种钻空子的方式让我们可以把double类型数据和指针相互转换#define _CRT_SECURE_NO_WARNINGS #includestdio.h #include stdint.h int main() { int a 0; char b a; double c 5.5; int* pa a; char* pb b; double* pc c; // 将指针转换为 double通过 uintptr_t c (double)(uintptr_t)pa; c (double)(uintptr_t)pb; c (double)(uintptr_t)pc; // 将 double 转换回指针通过 uintptr_t pa (int*)(uintptr_t)c; pb (char*)(uintptr_t)c; pc (double*)(uintptr_t)c; }我指针转uintptr_t可以吧这可是你语法规定的可以通过它安全转换。那我把uintptr_t转换成double更没问题了吧基础类型之间可以互相转换。我double转uintptr_t没问题吧基础类型之间的转换那我把uintptr_t类型转指针也没问题吧我现在又不是double类型凭什么不让我转指针。C语言类型转换规则基础类型非指针类型之间可以隐式类型转换指针类型之间可以隐式类型转换包括void*指针类型与非指针类型之间可以相互隐式类型转换但是转换的一方不能是double类型不过可以钻空子C中的类型转换有了上面的实验经历相信读者也能独立对c中的类型转换做实验了下面我给出所得结论基础类型intdoublechar...之间可以隐式类型转换。指针类型之间无法隐式类型转换。指针类型与基础类型之间无法隐式类型转换。指针类型之间可以强制类型转换。指针类型与基础类型之间可以强制类型转换但是涉及double类型的转换仍是不被允许的。在c中任意指针类型都可以隐式转换成void*类型但是void*类型转换成任意指针类型都需要强制类型转换。可以看到相对于C语言来说C的类型转换限制要多一点。C新增的四种强制类型转换四种强制类型转换语法为static_castnew_type(expression)dynamic_castnew_type(expression)const_castnew_type(expression)reinterpret_castnew_type(expression)上行转换和下行转换上行转换就是把子类指针或者引用转换成父类指针或引用下行转换就是把父类指针或引用转换成子类指针或者引用C语言上面的说法因为C语言面向过程没有对象的概念其实无论是上行转换还是下行转换编译器在编译时期都会根据c对象模型给被转换的指针添加偏移量从而让其正确的指向对象中父类或者子类的部分。c对象模型请看揭秘C对象模型虚表与内存布局下行转换不安全是指如果父类指针实际指向的对象并非子类类型而就是一个父类对象或者对象已销毁。那么此时指针偏移后访问这个对象的子类部分就会越界或者程序会进入未定义行为UB等总之这是不对的。而这样的检查在编译器时无法确定的只能在程序运行中进行检查。上行转换是安全的因为子类对象一定包含父类对象所以不会出现上面所说的情况如何解决这种不安全的问题下面就会介绍到。static_caststatic_cast把expression转换为new_type类型它主要有如下几种用法用于上行转换下行转换是不安全的。用于基础数据类型之间的转换如把int转换成char。把nullptr转换成目标类型的nullptr如int*p static_castint*(nullptr)。用于void*类型与普通指针类型的互转。把任何类型的表达式转换成void类型如static_castvoid(x),这主要用于消除“定义了变量而不使用”时爆出的警告。注意static_cast不能转换掉expression的const、volatile、或者__unaligned属性。static_cast不用于普通指针类型之间的转换也不能用于指针与基础类型之间的转换除了上述的特例。使用static_cast进行上行转化时尤其在多继承时会自动计算偏移量使指针正确指向要转化的父类当然很多转换方法都会这样做这并不是它特有的属性。在这儿提起主要是与后面的reinterpret_cast做区分。使用static_cast进行下行转化时则不会有任何额外检查或计算偏移量直接转换所以它不安全。dynamic_castdynamic_cast主要用于在类层次关系中继承关系上行转换下行转换和交叉转换多继承下基类与基类之间的转换它会在运行时检查转换是否安全。不过dynamic的使用是有条件的使用dynamic_cast进行转换的基类中一定要有虚函数否则编译不通过。基类中需要检测有虚函数的原因类中存在虚函数就说明它有想要让基类指针或引用指向派生类对象的情况此时转换才有意义。而且最重要的是运行时类型检查需要运行时类型信息而这个信息存储在类的虚函数表中。注意dynamic_cast如果转换指针失败的话返回的是0如果转换引用失败会抛出异常在类层次间进行上行转换时dynamic_cast和static_cast的效果是一样的。在进行下行转换时dynamic_cast具有类型检查的功能比static_cast更安全。下行转换需要被转换的指针实际指向的对象就是子类对象。而交叉转换需要被转换的指针实际指向子类对象且new_type和expression的类型两个都是该子类的基类类型。接下来我将举例说明下行转换和交叉转换class A { public: virtual void func() {} }; class B { public: virtual void func2() {} }; class C:public A,public B { }; int main() { //下行转换 A* a new C; C* ptr dynamic_castC*(a); if (ptr) { cout 转换成功 endl; }//走这儿 else { cout 转换失败 endl; } A* b new A; C* ptr1 dynamic_castC*(b); if (ptr1) { cout 转换成功 endl; } else { cout 转换失败 endl; }//走这儿b实际指向的不是子类对象 try { C temp; A c temp; C ptr3 dynamic_castC(c); } catch (...) { cout 转换失败-1 endl; } try { A temp; A d temp; C ptr4 dynamic_castC(d); } catch (...) { cout 转换失败-2 endl;//走这儿d实际指向的不是子类对象 } //交叉转换 A* e new C; B* ptr5 dynamic_castB*(e); if (ptr) { cout 转换成功 endl; }//走这儿 else { cout 转换失败 endl; } }reinterpret_cast在C语言中reinterpret_cast没有检查就是纯重新解释类型主要有两种强制转换用途指针类型之间的转换指针类型与基础类型之间的转换浮点类型与指针类型的转换除外会报编译错误无效的类型转换int main() { int x 95; int* p x; int y reinterpret_castint(p);//指针-整型 int * pp reinterpret_castint*(x);//整型-指针 char* ptr reinterpret_castchar*(p);//指针-指针 }另外static_cast和reinterpret_cast的区别主要在于多重继承比如class A { public: int m_a; }; class B { public: int m_b; }; class C : public A, public B {}; int main() { C c; printf(%p, %p, %p, c, reinterpret_castB*(c), static_cast B*(c)); }前两个的输出值是相同的最后一个则会在原基础上偏移4个字节这是因为static_cast计算了父子类指针转换的偏移量并将之转换到正确的地址c里面有m_a,m_b转换为B*指针后指到m_b处而reinterpret_cast却不会做这一层转换他只是重新解释了类型别的什么都不管。所以特别注意reinterpret_cast是一个异类几乎只有他不保证上行转换的安全。const_cast在C语言中const限定符通常被用来限定变量用于表示该变量的值不能被修改。而const_cast则正是用于强制去掉这种不能被修改的常数特性但需要特别注意的是const_cast不是用于去除变量的常量性而是去除指向常数对象的指针或引用的常量性其去除常量性的对象必须为指针或引用。除此之外它还可以去除volatile属性如果volatile修饰一个变量那么每次使用这个变量都只能从内存中取值而不能从cpu缓存中取防止优化。当然它也防止其他一些优化这里不加多言。常量指针被转化成非常量指针仍然指向原来的对象。举个例子int main() { const int x 10; int* p const_castint*(x); *p 50; cout p : x endl; cout *p : x endl; }可以发现常量指针被转化成非常量指针确实仍然指向原来的对象地址是一样的。但是*p和x打印的值居然不一样同一个空间存放两个值其实x的值是从寄存器取出来的并不是直接取自内存这是编译器故意这样做的。因为毕竟x已经定义成了常量也就表示了不可修改如果后面我们可以通过const_cast取消常性并随意更改那么把x定义成常量就没有意义了。所以把x的值存在寄存器从寄存器取这样每次访问x都是原来的值保持其常性而访问*p就是我们修改的值。既然不想修改本来是常量的变量的内容那么const_cast有什么用呢const int* Search(const int* a, int n, int val); int main() { int a[10] { 0,1,2,3,4,5,6,7,8,9 }; int val 5; int* p; p const_castint*(Search(a, 10, val)); if (p NULL) cout Not found the val in array a endl; else cout hvae found the val in array a and the val *p endl; return 0; } const int* Search(const int* a, int n, int val) { int i; for (i 0; i n; i) { if (a[i] val) return a[i]; } return NULL; }上述代码中我们定义了一个函数用于在a数组中寻找val值如果找到了就返回该值的地址如果没有找到则返回NULL。函数Search返回值是const指针假设不能去掉const这将会导致search返回指针后指针指向的内容在main函数中无法修改。当我们在a数组中找到了val值的时候我们会返回val的地址而此时我们可以去掉返回的指针的const属性而且a数组本身在main函数中并不是const因此我们就可以在main函数中对val值进行修改了。了解了const_cast的使用场景后可以知道使用const_cast通常是一种无奈之举同时也建议大家在今后的C程序设计过程中一定不要利用const_cast去掉指针或引用的常量性并且去修改原始变量的数值这是一种非常不好的行为。参考C 四种强制类型转换 - 静悟生慧 - 博客园static_cast, dynamic_cast, reinterpret_cast, const_cast区别比较 - Jerry19880126 - 博客园C强制类型转换static_cast、dynamic_cast、const_cast、reinterpret_cast - SpartacusIn21 - 博客园