C/C++ 类型转换

C/C++中的类型转换主要分为:隐式类型转换 & 显示类型转换(即强制类型转换)

隐式类型转换

在 C Primer Plus 中对隐式类型转换介绍了一下几大规则:

  1. 在表达式中,unsigned 和 signed 的 char 和 short 都会被系统自动转成 int,当 short 和 int 内存大小相同时(比如16 bit操作系统)unsigned short 会被转成 unsigned int(因为这个时候 unsigned short 比 int 大)

  2. 包含两种数据类型的任意运算中,两个值会被分别转成两种类型中的高级别的数据类型

  3. 类型的级别从低到高:long double > double > float > unsigned long long > long long > unsigned long > long > unsigned int > int,有一个特殊情况,当 long 和 int 的大小相同时,unsigned int 比 long 级别高,还有就是 short 和 char 类型没有出现是因为它们已经被 OS 转成了 int 或 unsigned int,(如:混合运算转换过程 $3+4/5.0F+6-9.0$,先计算 $4/5.0F$,4 转成 float 参与运算得到 0.8F,$3+0.8F$,3 转成 float 参与运算得到 3.8F,$3.8F+6$ 得到 9.8F,$9.8F-9.0$ 因为浮点数默认是 double 型,9.8F 被转成 double 型 9.8 参与运算得到 double 型 0.8,可以看出混合运算转换过程是一步一步进行的)

  4. 在赋值语句中,会被转换成赋值运算符左侧的类型,可能升级,也可能降级,不考虑四舍五入

  5. 作为函数参数时,char 和 short 会被自动转换成 int,float 会被转成 double

注意

隐式数据类型转换是 OS 在上述 5 种情况下自发进行的,对 coding 者来说是透明的,这里我没有继续深究,不再过多介绍,有些编译器可能在非上述情况下也进行了自动数据类型转换,那不是隐式数据类型转换,是编译器提供的便利,隐式数据类型转换只涉及基本的数据类型,指针不进行隐式数据类型转换

显示类型转换(即强制类型转换)

形式是 (type)data; 即小括号后面的数据被转成小括号内的数据类型

  1. 强转基本数据类型,12 + 12.2; 如果不进行强制类型转换,系统将自动进行隐式数据类型转换,转成两个数据类型中较高(占内存较大)的数据类型,也就是 double,这样也以最大限度的保证计算的精度,也可以进行强转,(如:12 + (int)12.2; 会把 12.2 先转成整型 12,然后再进行加法操作)

  2. 强转指针类型/强转地址类型,进行指针/地址类型的转换,需要注意两点,一是指针的类型,决定了指针的读写方式(也就是一次可以操作多少字节的数据),另一点是一定不要越界访问

  int a = 12;
  double *p = (double *)&a;
  *p = 12.3;

这样的操作是非法的,int a; 只有 4Byte,double *p 一次可以操作 8Byte,明显是越界操作了

  int a = 12;
  float *p = (float *)&a;
  *p = 23.2;

这样的操作是合法的(int 和 float 都占 4Byte),但因为 float 和 int 在内存中的存储方式不同,所以输出的数据可能与想象中的结果不同,但这是合法的操作

  double d = 12.3;
  int *p = (int *)&d;
  *p = 34;                       // 操作前 4Byte
  *(p + 1) = 45;                 // 操作后 4Byte
  *(int *)((short*)p + 1) = 56;  // 操作中间 4Byte

可以看出指针的操作是很灵活的,但是一定注意不要越界进行读写操作

C++11 中引入的四种强制类型转换方式

static_cast

用法:static_cast< type_name >(expression)

int *const i; // 顶层 const 表示指针本身是常量, 指针的指向不能变
const int *i; // 底层 const 表示指针所指向的对象是常量
CFather father; // 父类
CSon son;       // 子类
CFather* sonToFather = static_cast<CFather*>(&son);    // 合法的
CSon* fatherToSon = static_cast<CSon*>(&father);       // 合法的,可能不安全
COther* fatherToOther = static_cast<COther*>(&father); // 非法的
  1. 基本数据类型转换,例如:enum 转 int,int 转 enum,double 转 int 等

  2. 也可用于编译器无法自动进行的类型转换

int nNum = 10;
void* pTmp = static_cast<void*>(&nNum); // 任意非常量对象的地址存入 void*
int* pNum = static_cast<int*>(pTmp);    // 将 void* 转回初始的指针类型

dynamic_cast

用法:dunamic_cast< type_name >( expression ) 在父类和子类之间进行安全的上行和下行转换

适用情况

CFather father;
CSon son;
CSon* fatherToSon = dynamic_cast<CSon*>(&father);    // 父类转子类,必须是多态的情况
CFather* sonToFather = dynamic_cast<CFather*>(&son); // 子类转父类
CSon* sonToSon = dynamic_cast<CSon*>(&son);          // 相同类型之间的转换

const_cast

用法:const_cast< type_name >( expression )

const CFather *father = new CFather;
CFather* nonconstFather = const_cast<CFather*>(father); // 合法
CSon* nonconstCSon = const_cast<CSon*>(father);         // 非法
delete father;

reinterpret_cast

用法:reinterpret_cast< type_name >(expression)

struct Data {
  short a;
  short b;
};

int main() {
  long nNum = 16909320; // 0000 0001 0000 0010 0000 0100 0000 1000
  Data *data = reinterpret_cast<Data*>(&nNum);
  // a == 1032;(0000 0100 0000 1000)
  // b == 258; (0000 0001 0000 0010)
  cout << data->a << " " << data->b << endl;
}

reinterpret_cast 注意:

Table of Contents