C++从零开始(四)——赋值操作符(2)
类型转换
在《C++从零开始(二)》中说过,数字可以是浮点数或是整型数或其他,也就是说数字是具有类型的。注意《C++从零开始(三)》中对类型的解释,类型只是说明如何解释状态,而在前面已经说过,出于方便,使用二进制数来表示状态,因此可以说类型是用于告诉编译器如何解释二进制数的。
所以,一个长整型数字是告诉编译器将得到的二进制数表示的状态按照二进制补码的格式来解释以得到一个数值,而一个单精度浮点数就是告诉编译器将得到的二进制数表示的状态按照IEEE的real*4的格式来解释以得到一个是小数的数值。很明显,同样的二进制数表示的状态,按照不同的类型进行解释将得到不同的数值,那么编译器如何知道应该使用什么类型来进行二进制数的解释?
前面已经说过,数字是一种很特殊的操作符,其没有操作数,仅仅返回由其类型而定的二进制数表示的状态(以后为了方便,将“二进制数表示的状态”称作“二进制数”)。而操作符就是执行指令并返回数字,因此所有的操作符到最后一定执行的是返回一个二进制数。这点很重要,对于后面指针的理解有着重要的意义。
先看15;,这是一条语句,因为15是一个数字。所以15被认为是char类型的数字(因为其小于128,没超出char的表示范围),将返回一个8位长的二进制数,此二进制数按照补码格式编写,为00001111.
再看15.0f,同上,其由于接了“f”这个后缀而被认为是float类型的数字,将返回一个32位长的二进制数,此二进制数按照IEEE的real*4格式编写,为1000001011100000000000000000000.
虽然上面15和15.0f的数值相等,但由于是不同的类型导致了使用不同的格式来表示,甚至连表示用的二进制数的长度都不相同。因此如果书写15.0f == 15;将返回0,表示逻辑假。但实际却返回1,为什么?
上面既然15和15.0f被表示成完全不同的两个二进制数,但我们又认为15和15.0f是相等的,但它们的二进制表示不同,怎么办?将表示15.0f的二进制数用IEEE的real*4格式解释出15这个数值,然后再将其按8位二进制补码格式编写出二进制数,再与原来的表示15的二进制数比较。
为了实现上面的操作,C++提供了类型转换操作符——“()”。其看起来和括号操作符一样,但是格式不同:(<类型名>)<数字>或<类型名>(<数字>)。
上面类型转换操作符的<类型名>不是数字,因此其将不会被操作,而是作为一个参数来控制其如何操作后面的<数字>.<类型名>是一个标识符,其唯一标识一个类型,如char、float等。类型转换操作符的返回值就如其名字所示,将<数字>按照<类型名>标识的类型来解释,返回类型是<类型名>的数字。因此,上面的例子我们就需要如下编写:15 == ( char )15.0f;,现在其就可以返回1,表示逻辑真了。但是即使不写( char ),前面的语句也返回1.这是编译器出于方便的缘故而帮我们在15前添加了( float ),所以依然返回1.这被称作隐式类型转换,在后面说明类的时候,还将提到它。
某个类型可以完全代替另一个类型时,编译器就会进行上面的隐式类型转换,自动添加类型转换操作符。如:char只能表示-128到127的整数,而float很明显地能够表示这些数字,因此编译器进行了隐式类型转换。应当注意,这个隐式转换是由操作符要求的,即前面的“==”要求两面的数字类型一致,结果发现两边不同,结果编译器将char转成float,然后再执行“==”的操作。注意:在这种情况下,编译器总是将较差的类型(如前面的char)转成较好的类型(如前面的float),以保证不会发生数值截断问题。如:-41 == 3543;,左边是char,右边是short,由于short相对于char来显得更优(short能完全替代char),故实际为:( short )-41 == 3543;,返回0.而如果是-41 == ( char )3543;,由于char不能表示3543,则3543以补码转成二进制数0000110111010111,然后取其低8位,而导致高8位的00001101被丢弃,此被称为截断。结果( char )3543的返回值就是类型为char的二进制数11010111,为-41,结果-41 == ( char )3543;的返回值将为1,表示逻辑真,很明显地错误。因此前面的15 == 15.0f;实际将为( float )15 == 15.0f;。
注意前面之所以会朝好的方向发展(即char转成float),完全是因为“==”的缘故,其要求这么做。下面考虑“=”:short b = 3543; char a = b;。因为b的值是short类型,而“=”的要求就是一定要将“=”右边的数字转成和左边一样,这样才能进行正确的内存的写入(简单地将右边数字返回的二进制数复制到左边的地址所表示的内存中)。因此a将为-41.但是上面是编译器按照“=”的要求自行进行了隐式转换,可能是由于程序员的疏忽而没有发现这个错误(以为b的值一定在-128到127的范围内),因此编译器将对上面的情况给出一个警告,说b的值可能被截断。为了消除编译器的疑虑,如下:char a = ( char )b;。这样称为显示类型转换,其告诉编译器——“我知道可能发生数据截断,但是我保证不会截断”。因此编译器将不再发出警告。但是如下:char a = ( char )3543;,由于编译器可以肯定3543一定会被截断而导致错误的返回值,因此编译器将给出警告,说明3543将被截断,而不管前面的类型转换操作符是否存在。
现在应该可以推出——15 + 15.0f;返回的是一个float类型的数字。因此如果如下:char a = 15 + 15.0f;,编译器将发出警告,说数据可能被截断。因此改成如下:char a = ( char )15 + 15.0f;,但类型转换操作符“()”的优先级比“+”高,结果就是15先被转换为char然后再由于“+”的要求而被隐式转成float,最后返回float给“=”而导致编译器依旧发出警告。为此,就需要提高“+”的优先级,如下:char a = ( char )( 15 + 15.0f );就没事了(或char( 15 + 15.0f )),其表示我保证15 + 15.0f不会导致数据截断。
应该注意类型转换操作符“()”和前缀“++”、“!”、负号“-”等的优先级一样,并且是从右向左计算的,因此( char )-34;将会先计算-34的值,然后再计算( char )的值,这也正好符合人的习惯。
下篇将针对数字这个特殊操作符而提出一系列的东西,因此如果理解了数字的意思,那么指针将很容易理解。
- 最新评论
