C的隐式类型转换

Posted veaxen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C的隐式类型转换相关的知识,希望对你有一定的参考价值。

这篇文章仅仅针对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类型大的数据类型。其实在进行运算时,是以表达式中最长类型为准的,将其他类型转换成该类型,具体的规则如下:

  1. 比int类型小的类型(char, signed char, unsigned char, short, unsigned short),先经过整型提升,提升为int类型,然后int类型再根据表达式中最长类型转换为该类型。
  2. int、long(4字节) -->> unsinged int、unsinged long(4字节) -->> long long (8字节)-->> unsigned long long (8字节)<<-- double(8字节) <<-- float(4字节) (32位系统中)
  3. float -->> int(32位系统中)
  4. 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型,故f→double型,i→double型,fd-i→double型。

(3)(ch/i) 和(fd-i)进行加运算,由于fd-i为double型,故ch/i→double型,ch/i+(f*d-i)→double型。

(4)由于result为int型,故ch/i+(f*d-i)→double→int,即进行截断与舍入,最后取值为整型。


以上是关于C的隐式类型转换的主要内容,如果未能解决你的问题,请参考以下文章

深入浅出JavaScript中的隐式转换

25.scala的隐式转换

C语言中的隐式类型转换

OpenCV中operator的隐式类型转换

C++ 类的隐式转换

不可不会的scala隐式转换