上次说了关于表达式的一些内容,说到还有一些关于数据类型转换的内容,今天我们接着八一八C++中的数据类型转换。
一、隐式类型转换
在表达式中,有些操作符可以对多种类型的操作数进行操作, 例如 + 操作符的操作数可以同时有int型,也可以有float型, 这就引入了一个问题到底应该由什么
决定表达式的值的类型。 例如:
3.1415926 + 5; //double类型 + 整型, 结果为什么类型呢?? 3.1415926 * 2 * 3 ; //double类型 * 整型 * 整型; 得到的表达式的值为什么类型呢?? 2 * 4UL; // 整型 * 无符号整型 , 得到的表达式的值的类型为什么呢??
在C++中,为了解决这类问题引入了类型转换的机制。
1、什么时候会发生类型转换
A: 在具有混合类型的表达式中,会发生类型转换, 如上面的例子。
3.14 * 4; 在计算的时候会发生类型转换,会将4转换为 4.0 然后与3.14 相乘。
B:用作条件的表达式会被转换为bool类型
int iVar; ....... if(iVar) .......
这里当做if语句的条件表达式的 iVar 会发生类型转换, 当iVar 非零时转换为 true, 当 iVar 为0时转换为false。
C: 当bool类型的变量、值不是 !、&&、|| 操作符的操作数时会发生类型转换
bool bVar; ....... int iVar; int iResult; iResult = iVar + bVar; //这里bool量bVar会转换为整型
如上所示, bool量bVar会发生类型转换, 当bVar为真True时转换为1, 当bVar为假false时会转换0, 这样就可以参与计算。
D:当赋值操作符的左右操作数的类型不一致时会发生类型转换
int iVar; iVar =3.1415926; //double类型的3.1415926会被转换为int型赋值给iVar;
这里讲double类型的值转换为int型赋值给int型
E:当其他类型的值初始化变量时会发生类型转换
int iVar = 3.1415926; //赋值初始化,或者 int iVar(3.1415926); //直接初始化
这里进行初始化时会将3.1415926 转换为int型,然后初始化iVar。
2、算术转换
C++为内置的操作符和数据类型提供了一套默认的数据类型转换机制,其中最为常见的就是算术转换。 算术操作符 + 、- 、* 、/ 两个操作数的类型不一致时
就会发生数据类型转换, 在程序执行前,会将算术操作符两边的操作数转换为同一数据类型后才能进行算术操作。算术转换的基本原则就是在转换后要能保证计算的
精度。 例如如果算术操作符的操作数有一个为long double 那么就会将另外一个操作数转换为long double类型然后进行计算。
在C语言中,会有如下基本的类型转换特点:
注意的是float类型不会自动转换为double类型,这一点需要特别的注意。在C++中大体保持了这样一个转换规则,我们可以通过sizeof操作符来看看操作结果。
Exp:
int main() { cout<<"sizeof(3.14L * 2) is:"<<sizeof(3.14L * 2)<<endl; cout<<"sizeof(3.14 * 2 ) is:"<<sizeof(3.14 * 2)<<endl; cout<<"sizeof(3.14f * 2) is:"<<sizeof(3.14f *2)<<endl; cout<<endl; cout<<"sizeof(long int) is"<<sizeof(long int)<<endl; cout<<"sizeof(‘a‘*‘a‘) is:"<<sizeof(‘a‘*‘a‘)<<endl; cout<<"sizeof(2L * 2) is:"<<sizeof(2L * 2)<<endl; return 0; }
程序的执行结果为:
[[email protected] cpp_src]# g++ test.cpp [[email protected] cpp_src]# ./a.out sizeof(3.14L * 2) is:12 sizeof(3.14 * 2 ) is:8 sizeof(3.14f * 2) is:4 sizeof(long int) is4 sizeof(‘a‘*‘a‘) is:4 sizeof(2L * 2) is:4
总结一句就是:
1、对于所有比int类型小的整型,在计算的过程中都会转换为int型,如果其值都包含在int内,则会将操作数转换为int型然后进行计算, 如果其值
超过了int型的表示范围,则会转换为 unsigned int型进行计算。 如果操作数中有long 型,则会转换为long类型进行计算。
2、对于浮点型,如果有long double则会转换为long double进行计算, 如果有double类型,则会转换为double类型进行计算, 如果有float则
会转换为float类型进行计算。
对于这个需要特别注意的就是float类型不会自动提升为double类型,这个是与整型不一样的地方。
3、关于有符号和无符号数之间的转换
讨论的是整型的无符号数和有符号数直接的转换, 这个是C/C++语言当中的一大难点, 前面我有一篇文章讨论过这个问题。这里再重新的描述一下。
A: 两个操作数为 unsigned short int 和 int
如果int足够保存unsigned short int 的值,那么就会转换int进行计算,如果不能保存,那么就会转换为unsigned int 进行计算。
B: 两个操作数为 unsigned int 和 long int
如果long int足够保存所有的unsigned int类型的值,那么就会转换为long int进行计算,否则就转换为unsigned long int 进行计算。
C: 两个操作数为int 和unsigned int
这个地方有点需要注意,int会转换为unsigned int 后进行计算, 计算的结果为unsigned int类型,然后如果赋值给int 类型的话,那么可能出现溢出
的情况, 通过截断高位部分将unsigned int 类型的值赋值给int类型。
这个地方一般也不太需要特别的注意,但是在一些特殊的地方则需要引起特别的关注。
4、关于指针和数组
在我的以前的一篇随笔中曾经描述过这个问题,那就是在一般的情况下,数组(名)都可以转换为指针。例如:
int iArray[3]; int *pInt = iArray; //数组(名)转换为指针类型,然后初始化指针变量pInt
这种转换基于下面的理念: 数组名一个数组的首地址,并且是个常量,不可改变,不可对数组名进行赋值。
要点:
1、sizeof(iArray) ; 这个地方,不会将数组名转换为指针。
2、定义引用的时候,不会将数组名转换为指针
Exp:
int main() { int iArray[5] = {1,2,3,4,5}; int (& refArray)[5] = iArray; cout<<"iArray[0]="<<refArray[0]<<endl; return 0; }
程序的执行结果如下:
[[email protected] cpp_src]# g++ test.cpp [[email protected] cpp_src]# ./a.out iArray[0]=1
5、关于bool类型的转换规则
其他非bool类型的内置类型如果要转换为bool值,则非零转换为真True, 0转换为假false。
bool类型会转换int类型, true转为1, false转为0
6、枚举类型转换规则
枚举类型是一种自定义的数据类型,通过关键字 enum来定义。枚举类型通常用来定义一些属性或者状态,比方说定义一些值表示星期, 定义一些值表示文件的
打开、关闭状态, 定义一些值表示忙、闲等等。
A: 定义枚举类型
语法: enum 枚举类型名 {枚举类型可以取的值};
Exp:
enum open_mode {input, output , append } // 定义了枚举类型 Week, 这种类型的变量可以取的值为: input、output、append enum open_mode status = input; //定义枚举类型变量 status,并初始化为input
要注意的是定义枚举类型的变量时,关键字 enum不能省略,除非用typedef为枚举类型定义了别名。
B:枚举类型值
默认定义枚举类型的时候,其第一个值默认取值为0, 后面的值其值会默认增加1,
例如:
enum file_status {close, open}; // 这里默认close的值为0 , open的值为1
还可以指定这些值,
例如:
enum file_status {close = 10 , open}; //这里 close = 10 , 而open的值为11
要点:
指定枚举的值时可以有两个值相等。
enum Points {point2d = 2, point2w, point3d= 3, point3w}; //这一点需要引起注意, 但是一般不会这么定义枚举数据类型
C:枚举类型的转换规则
枚举类型可以参与计算, 可以将其值转换为int型的值, 其转换后的值,根据枚举类型的值确定。
typedef enum open_mode {input, output,append} OPENMODE; OPENMODE eVar = input; int iVar = 10 + eVar ; // 则eVar 转换为int型的值0 ,然后计算表达式的值
二、显示类型转换
显示类型转换就是在程序中明确的指出类型转换规则, 这样可以覆盖系统默认的隐式转换规则。隐式转换有: dynamic_cast、static_cast、const_cast、
reinterpret_cast.
1、dynamic_cast
支持运行时识别指针或引用执行的对象,待后续专门随笔进行介绍。
2、const_cast
const_cast 就是将const对象的const属性强制的取消,使我们可以显示的修改对象的值。这里没有搞清楚, 不知道哪位大神,对这里理解比较透彻,请
不吝指教。
3、static_cast
static_cast和C语言中强制类型转换一样, 只是表示方法不一样而已。 当然C++也支持C语言风格的强制类型转换。
char chVar = static_cast<char>(1234); //显示的将1234转换为char类型。
这里要注意的static_cast后面用<> 将要转换为的数据类型表示出来,而用( )操作符将要转换的操作数括起来。
C语言风格的强制类型转换:
char chVar = (char)1234;
这里不进行过多的讨论。
4、reinterpret_cast
reinterpret_cast通常为操作数的位模式提供重新解释的机制。
例如:
int iVar; int *pInt = &iVar; *pInt = 12345; char *pCh = reinterpret_cast<char *>(pInt);
这里我们如果我们通过pCh访问内存的话, 那么系统就会将变量 iVar占用的内存 4个字节(32bit的系统)解释为char型数组;值得注意的是iVar 和 *pInt 访问
这一块内存区域的时候,还是会将这一块区域解释为int型, 就是说强制的 reinterpret_cast 并不能改变原指针指向的数据类型或者原变量的数据类型。这里特别需要
注意。
关于类型转换暂时就这么多的内容,其他内容待续...........