C++_系列自学课程_第_11_课_类型转换_《C++ Primer 第四版》

  上次说了关于表达式的一些内容,说到还有一些关于数据类型转换的内容,今天我们接着八一八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 并不能改变原指针指向的数据类型或者原变量的数据类型。这里特别需要

注意。

  关于类型转换暂时就这么多的内容,其他内容待续...........

时间: 2024-12-16 21:31:49

C++_系列自学课程_第_11_课_类型转换_《C++ Primer 第四版》的相关文章

C++_系列自学课程_第_7_课_数组_《C++ Primer 第四版》

说到数组,大家应该都很熟悉,在C.Pascal.Java等语言中,都有数组的概念.在C++中也提供了对数组的支持.数组简单来说就是一堆相同 数据类型对象的集合. 这里要把握住两个要点: 相同的数据类型.集合. 一.数组 1.定义 数组是由数组存储的对象的类型名.标识符和数组维数组成一种复合数据类型. 类型名规定了可以存储在数组中的对象的类型, 标识符用来标识 数组,用来在程序中引用数组元素, 维数用来规定数组可以存放多少数组元素. 数组的定义如下所示: 类型名 数组标识符[n]; 下面是一些数组

C++_系列自学课程_第_6_课_bitset集_《C++ Primer 第四版》

在C语言中要对一个整数的某一个位进行操作需要用到很多的技巧.这种情况在C++里面通过标准库提供的一个抽象数据类型 bitset得到了改善. 一.标准库bitset类型 1.bitset的作用 bitset可以看成bit的集合,可以单独的访问集合中的某一位,访问的结果可以作为逻辑的判断的条件.使用bitset的时候可以 不关心这些bit的存储方式,而通过bitset类型提供的一套接口进行操作. 和string和vector一样,要使用bitset类型,需要报备,如下所示: #include <bi

C++_系列自学课程_第_3_课_变量和基本类型_《C++ Primer 第四版》

最近复习C++相关内容,决定在这里记录自己复习的过程. 以前写过部分文字,但是没有坚持连续写,因此学完后 基本又忘光啦,主要是没有实践,这一次决定自学完后,在这里在复习一遍增强自己的记忆和理解程度. 一.基本内置类型 C++语言内置了一些数据类型,主要有两大类: 整型和浮点型. 整型又有:  bool型.char型.wchar_t型.short型,int型,long int型. 浮点型有: float型, double型.long double型(扩展双精度型). 我们知道不同的数据类型决定了不

C++_系列自学课程_第_5_课_vector容器_《C++ Primer 第四版》

再一次遇到 vector 这个单词; 每一次见到这个单词都感觉这个单词非常的 "高大上"; 数字遇到vector马上就可以360度旋转: 当 "电" 遇到vector,马上让交流可以变得和直流一样进行控制(德国电气工程师的矢量控制理论,目前在工控界对电机控制应用 非常广泛,是变频器控制的基础理论,可惜的是中国目前没有这方面的真正的专家, 就是IT行业中的TI公司的TMS320LF24xx系列 DSP做的事,中国的基础理论的研究真的是落后于西方发达国家很多年),而在C

C++_系列自学课程_第_8_课_指针和引用_《C++ Primer 第四版》

C语言最富有迷幻色彩的部分当属指针部分,无论是指针的定义还是指针的意义都可算是C语言中最复杂的内容.指针不但提供给了程序员直接操作硬件部分的操作接口,还提供给了程序员更多灵活的用法.C++继承这一高效的机制,同时引入了另一个与指针相似但不相同的机制: 引用. 一.引用 简单的来说,引用就是变量的别名(alias), 通过别名我们可以操作引用代表的变量. 定义一个引用的语法如下所示: 变量类型   &引用标识符 = 变量名. Exp: int iVar=10; int &iRef = iVa

C++_系列自学课程_第_4_课_string_《C++ Primer 第四版》

相信学习过计算机编程的人,基本应该都接触过C语言,在C语言中处理字符串大家一定多遇到过, 也都知道处理字符串非常麻烦,而在C++里面,由标准库string类提供对可变长的字符串的支持.下面 来看看string类的特性. 1.使用的先决条件 在使用标准库之前必须包含相应的头文件, 要使用string类型,就需要包含相应的头文件,如下所示: #include <string> 在使用iostream的时候,我们未来引用iostream里面的类,前面需要加上  std::  来表示引用的是标准 IO

C++_系列自学课程_第_10_课_表达式_《C++ Primer 第四版》

程序设计语言中大部分程序都在进行表达式的求值操作, 例如求两个数的和,求一个表达式的逻辑结果,或者通过输入输出表达式语句进行输入和输出. 这里我们对表达式进行讨论. 一.表达式 1.表达式 表达式由一个操作数或者多个操作数同操作符组合而成: 字面值是一个最简单的表达式.  表达式都会产生一个结果,如果表达式中没有操作符,则表达式 的值是操作数本身, 如果表达式中有操作符,则表达式的结果是操作符对操作数进行操作后的结果. 一般而言表达式的返回的是右值,不能给表达式赋值, 但可以获取表达式的值. E

C++_系列自学课程_第_12_课_语句_《C++ Primer 第四版》

前面的文章说完了表达式和类型转换的部分内容,在我参考的书里面,接下来讨论的是各种语句,包括:顺序语句.声明语句.复合语句(块语句).语句作用域 .if语句.while语句.for语句.do...while语句.break语句.continue语句.goto语句.try语句. 这里我们来讨论这些语句,为了方便讨论和记忆,我们可以将这些语句可以分为五大类:   简单语句.条件语句.循环语句.流程控制语句.异常处理.当然也可以 不这么分类,我这么说就是为了自己好理解和记忆. 一.简单语句 我将顺序语句

C++_系列自学课程_第_12_课_结构体

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 struct CDAccount 7 { 8 double balance; //余额 9 double rate; //利息 10 int term; //存期 11 }; 12 13 struct Date 14 { 15 int year; 16 int month; 17 int day; 18 }; 19 20 struct Per