C++标准转换运算符reinterpret_cast
C++标准转换运算符reinterpret_cast
reinterpret_cast <new_type> (expression)
reinterpret_cast运算符是用来处理无关类型之间的转换;它会产生一个新的值,这个值会有与原始参数(expressoin)有完全相同的比特位。
什么是无关类型?我没有弄清楚,没有找到好的文档来说明类型之间到底都有些什么关系(除了类的继承以外)。后半句倒是看出了reinterpret_cast的字面意思:重新解释(类型的比特位)。我们真的可以随意将一个类型值的比特位交给另一个类型作为它的值吗?其实不然。
IBM的C++指南里倒是明确告诉了我们reinterpret_cast可以,或者说应该在什么地方用来作为转换运算符:
- 从指针类型到一个足够大的整数类型
- 从整数类型或者枚举类型到指针类型
- 从一个指向函数的指针到另一个不同类型的指向函数的指针
- 从一个指向对象的指针到另一个不同类型的指向对象的指针
- 从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针
- 从一个指向类数据成员的指针到另一个指向不同类型的数据成员的指针
不过我在Xcode中测试了一下,事实上reinterpret_cast的使用并不局限在上边所说的几项的,任何类型的指针之间都可以互相转换,都不会得到编译错误。上述列出的几项,可能 是Linux下reinterpret_cast使用的限制,也可能是IBM推荐我们使用reinterpret_cast的方式
所以总结来说:reinterpret_cast用在任意指针(或引用)类型之间的转换;以及指针与足够大的整数类型之间的转换;从整数类型(包括枚举类型)到指针类型,无视大小。
(所谓"足够大的整数类型",取决于操作系统的参数,如果是32位的操作系统,就需要整形(int)以上的;如果是64位的操作系统,则至少需要长整形(long)。具体大小可以通过sizeof运算符来查看)。
reinterpret_cast有何作用
从上边对reinterpret_cast介绍,可以感觉出reinterpret_cast是个很强大的运算符,因为它可以无视种族隔离,随便搞。但就像生物的准则,不符合自然规律的随意杂交只会得到不能长久生存的物种。随意在不同类型之间使用reinterpret_cast,也之后造成程序的破坏和不能使用。
比如下边的代码typedef int (*FunctionPointer)(int);
int value = 21;
FunctionPointer funcP;
funcP = reinterpret_cast<FunctionPointer> (&value);
funcP(value);
我先用typedef定义了一个指向函数的指针类型,所指向的函数接受一个int类型作为参数。然后我用reinterpret_cast将一个整型的地址转换成该函数类型并赋值给了相应的变量。最后,我还用该整形变量作为参数交给了指向函数的指针变量。
这个过程编译器都成功的编译通过,不过一旦运行我们就会得到"EXC_BAD_ACCESS"的运行错误,因为我们通过funcP所指的地址找到的并不是函数入口。
由此可知,reinterpret_cast虽然看似强大,作用却没有那么广。IBM的C++指南、C++之父Bjarne Stroustrup的FAQ网页和MSDN的Visual C++也都指出:错误的使用reinterpret_cast很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使用reinterpret_cast方式。
这样说起来,reinterpret_cast转换成其它类型的目的只是临时的隐藏自己的什么(做个卧底?),要真想使用那个值,还是需要让其露出真面目才行。那到底它在C++中有其何存在的价值呢?
MSDN的Visual C++ Developer Center 给出了它的使用价值:用来辅助哈希函数。下边是MSNDN上的例子:
// expre_reinterpret_cast_Operator.cpp
// compile with: /EHsc
#include <iostream>
// Returns a hash code based on an address
unsigned short Hash( void *p ) {
unsigned int val = reinterpret_cast<unsigned int>( p );
return ( unsigned short )( val ^ (val >> 16));
}
using namespace std;
int main() {
int a[20];
for ( int i = 0; i < 20; i++ )
cout << Hash( a + i ) << endl;
}
//如果跟我一样是64位的系统,可能需要将unsigned int改成 unsigned long才能运行。
这段代码适合体现哈希的思想,暂时不做深究,但至少看Hash函数里面的操作,也能体会到,对整数的操作显然要对地址操作更方便。在集合中存放整形数值,也要比存放地址更具有扩展性(当然如果存void *扩展性也是一样很高的),唯一损失的可能就是存取的时候整形和地址的转换(这完全可以忽略不计)。
不过可读性可能就不高,所以在这种情况下使用的时候,就可以用typedef来定义个指针类型:typedef unsigned int PointerType;
这样不是更棒,当我们在64位机器上运行的时候,只要改成:typedef unsigned long PointerType;
当reinterpret_cast面对const
IBM的C++指南指出:reinterpret_cast不能像const_cast那样去除const修饰符。 这是什么意思呢?代码还是最直观的表述:
int main()
{
typedef void (*FunctionPointer)(int);
int value = 21;
const int* pointer = &value;
//int * pointer_r = reinterpret_cast<int*> (pointer);
// Error: reinterpret_cast from type 'const int*' to type 'int*' casts away constness
FunctionPointer funcP = reinterpret_cast<FunctionPointer> (pointer);
}
例子里,我们像前面const_cast一篇举到的例子那样,希望将指向const的指针用运算符转换成非指向const的指针。但是当实用reinterpret_cast的时候,编译器直接报错组织了该过程。这就体现出了const_cast的独特之处。
但是,例子中还有一个转换是将指向const int的指针付给指向函数的指针,编译顺利通过编译,当然结果也会跟前面的例子一样是无意义的。
如果我们换一种角度来看,这似乎也是合理的。因为const int* p = &value;
int * const q = &value;
这两个语句的含义是不同的,前者是"所指内容不可变",后者则是"指向的地址不可变"(具体参考此处)。因此指向函数的指针默认应该就带有"所指内容不可变"的特性。
毕竟函数在编译之后,其操作过程就固定在那里了,我们唯一能做的就是传递一些参数给指针,而无法改变已编译函数的过程。所以从这个角度来想,上边例子使用reinterpret_cast从const int * 到FunctionPointer转换就变得合理了,因为它并没有去除const限定
reinterpret_cast <new_type> (expression)
reinterpret_cast是强制类型转换符!他是用来处理无关类型转换的,通常为操作数的位模式提供较低层次的重新解释!但是他仅仅是重新解释了给出的对象的比特模型,并没有进行二进制的转换!
他是用在任意的指针之间的转换,引用之间的转换,指针和足够大的int型之间的转换,整数到指针的转换,在家面的文章中将给出!
来看一个例子:
OK, 在这里你可以看到reinterpret_cast的强大作用,但是要注意的是,他并没有进行二进制的转换,pc指向的真实对象其实还是int的,不是char~
- int *pi;
- char *pc = reinterpret_cast<char*>(pi);
对于reinterpret_cast运算符要谨慎使用,下面给出一些使用的地方:
参考IBM C++
- A pointer to any integral type large enough to hold it (指针转向足够大的整数类型)
- A value of integral or enumeration type to a pointer (从整形或者enum枚举类型转换为指针)
- A pointer to a function to a pointer to a function of a different type (从指向函数的指针转向另一个不同类型的指向函数的指针)
- A pointer to an object to a pointer to an object of a different type (从一个指向对象的指针转向另一个不同类型的指向对象的指针)
- A pointer to a member to a pointer to a member of a different class or type, if the types of the members are both function types or object types (从一个指向成员的指针转向另一个指向类成员的指针!或者是类型,如果类型的成员和函数都是函数类型或者对象类型)
这些是IBM C++推荐给我们的使用方式!reinterpret_cast是为了映射到一个完全不同类型的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄玄虚和其他目的,这是所有映射中最危险的。(这句话是C++编程思想中的原话)
大家使用的时候千万记住,不要乱使用!错误使用很容易导致程序的不安全!
Misuse of the reinterpret_cast operator can easily be unsafe. 这是MSDN 的原话!
当然reinterpret_cast对 const, volatile, or __unaligned 也是没有用处的!
MSDN 上面给了一个实际的用处:哈希函数辅助
- // expre_reinterpret_cast_Operator.cpp
- // compile with: /EHsc
- #include <iostream>
- // Returns a hash code based on an address
- unsigned short Hash( void *p ) {
- unsigned int val = reinterpret_cast<unsigned int>( p );
- return ( unsigned short )( val ^ (val >> 16));
- }
- using namespace std;
- int main() {
- int a[20];
- for ( int i = 0; i < 20; i++ )
- cout << Hash( a + i ) << endl;
- }
主要是涉及数据在内存中的表示。
看看下面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <iostream> using namespace std; void main() { int i = 875770417; //在内存中的表示从低位到高位依次是0x31 0x32 0x33 0x34 cout<<i<< " " ; char * p = reinterpret_cast < char *>(&i); for ( int j=0; j<4; j++) cout<<p[j]; cout<<endl; float f = 0.00000016688933; //在内存中的表示从低位到高位依次也是0x31 0x32 0x33 0x34 cout <<f<< " " ; p = reinterpret_cast < char *>(&f); for (j=0; j<4; j++) cout<<p[j]; cout<<endl; } |
不管是int型的i还是float型的f,经过reinterpret_cast<char*>(&addr)的转换后,输出都是"1234"
数据的表示相同,就看编译器怎么来解释数据,也就是语义表达的问题。
reinterpret_cast的转换就是不理会数据本来的语义,而重新赋予它char*的语义。
reinterpret_cast<char*>(&st) 这个怎么理解
reinterpret_cast是C++新标准下的强制类型转换,这里将&st的指针转换为char*型