这篇文章仅仅针对 C 语言存在的隐式类型转换做一些分析, 关于 C++ 的这方面研究, 有时间我再另外写一篇文章.
关于隐式类型转换, 是指发生在没有明确说明的情况下(C 语言风格的强制类型转换就是属于我们程序员有明确说明的), 编译器自动帮我们执行的类型转换.
通常同类型的数据进行运算, 比较和赋值的时候我们是不需要担心的, 这里我只是说明不同类型的数据进行运算, 比较和赋值时, 且我们程序员没有指定类型转换时, 编译器是如何帮我们进行处理类型之间的转换的, 只有知道这个过程, 才能让我们知道程序设计时应该注意和避免的地方.
一, 比 int 类型小的隐式类型转换: 整型提升
想要知道隐式类型转换, 我们有必要了解一下整型提升(Integer Promotion), 这也是属于隐式类型转换的一种方式.
整型提升是 C 程序设计语言中的一项规定: 在表达式计算时 (包括比较运算和算术运算等), 比 int 类型小的类型(char, signed char, unsigned char, short, unsigned short 等) 首先要提升为 int 类型, 然后再执行表达式的运算.
具体的我们从下面的程序进行分析吧
- #include <stdio.h>
- int main()
- {
- signed char a = -1;
- unsigned char b = a;
- if(a == b)
- printf("a==b");
- else if(a<b)
- printf("a<b");
- else
- printf("a>b");
- return 0;
- }
执行结果我想很多人都可以想到是: a<b
但是编译器是怎么处理这个过程的呢?? 答案就是整型提升. 对比 int 类型小的类型的每一次表达式计算都伴随整型提升, 所以在对 char 型的变量进行比较运算时(a> b,a <b,a==b 这些比较运算), 编译器首先会对 char 型变量进行 Integer Promotion, 也就是将 char 型变量提升成 int 类型的, 然后再进行比较.
至于提升的方法, 是根据原始类型进行位扩展 (如果原始类型为 unsigned char, 进行零扩展, 如果原始类型为 signed char, 进行符号位扩展) 到 32 位. 拿上文代码作为例子, a 是 signed char 型的, a=-1 在内存中的位储存形式是 0xff, 把 a 赋值给 b, 随意 b 在内存中的位储存形式也是 0xff; 然后就是 a 与 b 进行比较运算了, 编译器会把 a,b 都提升到 int 类型, 那么原来 a 在内存中的位形式是 0xff, 提升为 int 类型后会变成 0xffffffff(符号位扩展), 原来 b 在内存中的位形式是 0xff, 提示为 int 类型后会变成 0x000000ff(零扩展), 可以看出, 此时 a 是小于 b 的. 等等? a < b? 对的, 没错, a 是小于 b 的, 因为 Integer Promotion 之后, a,b 都暂时 (只是暂时, 仅仅只是在执行运算时提升了) 提升为 int 类型了, 也就是 signed int 类型.
其实 char a += 1; 这个表达式就已经隐含了 Integer Promotion.
为什么编译器要进行 Integer Promotion?
学过微机原理或者学习过汇编的同学可能会知道, 在我们的 CPU 中有一个算术逻辑单元 (Arithmetic&logical Unit), 简称 ALU, 主要功能是进行二位元的算术运算, 如加减乘(不包括整数除法) 和寄存器中的值之间的逻辑运算. 那么, 我们的 C/C++ 程序进行的运算最终也是要在 ALU 中进行的, 以 32 位 CPU 为例, 寄存器都是 32 位的(刚好是一个 int 类型所占用的位数), 想要把 char 类型的变量送进 ALU 中运算, 那必然是需要把 char 类型变成 32 位, 然后通过 32 位寄存器送入 ALU, 那么这个时候 Integer Promotion 的意义就出来了, 如果不这样进行提升, ALU 就无法对 char 类型的变量进行运算了.
二, 比 int 类型大的隐式类型转换
上面说了整型提升, 只是针对表达式中没有比 int 类型大的数据类型. 其实在进行运算时, 是以表达式中最长类型为准的, 将其他类型转换成该类型, 具体的规则如下:
比 int 类型小的类型(char, signed char, unsigned char, short, unsigned short), 先经过整型提升, 提升为 int 类型, 然后 int 类型再根据表达式中最长类型转换为该类型.
- int,long(4 字节) -->> unsinged int,unsinged long(4 字节) -->> long long (8 字节)-->> unsigned long long (8 字节)<<-- double(8 字节) <<-- float(4 字节) (32 位系统中)
- float -->> int(32 位系统中)
- double -->> long long
下面还是来看看一段程序, 与第一段程序做个对比
- #include <stdio.h>
- int main()
- {
- signed int a = -1;
- unsigned int b = a;
- if(a == b)
- printf("a==b");
- else if(a<b)
- printf("a<b");
- else
- printf("a>b");
- return 0;
- }
执行结果如下:
a==b
对比下第一段程序, 我们很快就能发现区别!
三, 赋值时的隐式类型转换
前面提到的都是在进行计算时存在的隐式类型转换, 还要特别说明的就是在进行赋值操作时, 赋值运算符右边的数据类型必须转换成赋值号左边的类型, 若右边的数据类型的长度大于左边, 则要进行截断或舍入操作.
下面用一实例说明:
- char ch;
- int i,result;
- float f;
- double d;
- result=ch/i+(f*d-i);
(1)首先计算 ch/i,ch int 型, ch/i int 型.
(2)接着计算 fd-i, 由于最长型为 double 型, 故 fdouble 型, idouble 型, fd-idouble 型.
(3)(ch/i) 和 (fd-i) 进行加运算, 由于 fd-i 为 double 型, 故 ch/idouble 型, ch/i+(f*d-i)double 型.
(4)由于 result 为 int 型, 故 ch/i+(f*d-i)doubleint, 即进行截断与舍入, 最后取值为整型.
来源: http://www.bubuko.com/infodetail-2644860.html